1

I would like to create a formset, where each form has a dropdown pointing to a set of sales items.

Model:

class SalesItem(models.Model):        
    item_description    = models.CharField(max_length=40)
    company             = models.ForeignKey(Company)

Here I create a form with a dropdown, hoping to pass in the company as a source for the dropdown. Hold on to this thought, as I think that is not possible in my scenario.

Form:

class SalesItemFSForm(Form):        
    sales_item      =   forms.ModelChoiceField(required=False, queryset = '')

    def __init__(self, company, *args, **kwargs):
        super(SalesItemFSForm, self).__init__(*args, **kwargs)
        self.fields.sales_item.queryset = company.salesitem_set.all()

Now within my view I would like to create a formset with this form:

formset_type = formset_factory(SalesItemFSForm, extra=0)

The problem becomes right away clear, as there seem to be no way that I could pass in the company to determine the source for the dropdown.

How am I supposed to do this?

Many Thanks,

Update:

it seems Jingo cracked it. :)

A ModelForm works better than a Form. On top of it I had to add fields = {} to SalesItemFSForm, to make sure that the SalesItem's fields are not showing up in the template. Because all we are interested in is our dropdown (SalesItem).

So far so good. But now I see as many dropdowns shown as I have Salesitems. It shouldn;t show any unless the user presses a jquery button.

And I think this is the problem, we should NOT pass in

formset_type = modelformset_factory(SalesItem, form=SalesItemFSForm, extra=0)

Because our form doesn't need any instance of the SalesItem. We need a dummy Model.

That was the reason I tried to solve it initially with classic Formset instead of ModelFormset. So its kind of half way there. :)

Update 2:

Jingo, good point. Effectively I was thinking of a custom save, where I just see how many formsets are added by the user via jQuery and save it myself within the view. Literally SalesItem is a ManyToMany field. But the standard M2m widget is horrible. Hence I wanted to replace it with formsets, where each salesItem is a dropdown. The user can then add as many dropdowns (forms in formset) to the page and submit them. Then I would add the relationship in the view.

class DealType(models.Model):    
    deal_name           = models.CharField(_(u"Deal Name"), max_length=40)
    sales_item          = models.ManyToManyField(SalesItem)    
    price               = models.DecimalField(decimal_places=2, max_digits=12)

Hope this makes it clear. Maybe there is an easier way to do this. :)

Btw I also found this excellent jquery snippet code how to add/remove forms to/from a formset.

Update 3:

Indeed when instantiating the object like this, we would only get one form in the formset and can add more via jquery. Perfect!! Unless there is an easier way to achieve this. :)

salesitem_formsets = formset_type(queryset=SalesItem.objects.filter(pk=1))

However this comes back hunting you in the request.POST, since you can't just do:

salesitem_formsets = formset_type(request.POST)

It still requires the queryset to be set. Tricky situation...

Houman
  • 64,245
  • 87
  • 278
  • 460
  • Same general issue as http://stackoverflow.com/questions/11692190/dynamic-modelchoicefield-queryset-with-an-inline-model-formset – supervacuo Aug 15 '12 at 18:30
  • @supervacuo The underlying issue is the same but working with different widgets. So you were trying to solve it with inline-formsets, interesting, but it seems you have no solution to this problem either, correct? – Houman Aug 15 '12 at 18:41
  • When you instantiate the form (ie: formset_type()), you can pass a in an empty queryset like (normally Django goes for ".all()"). Like: formset_type(queryset=SalesItem.objects.none()). See here https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset, wait no, still not right :/ – Jingo Aug 15 '12 at 22:03
  • So, in my thinking the cat bites itself in the tail a little bit. Is it really the SalesItem which has to be used for the forms of the formset?? – Jingo Aug 15 '12 at 22:18
  • Jingo, apologies, i just saw your comments here. I have updated the question. – Houman Aug 15 '12 at 22:42
  • My apologies too, I was tired an fell asleep :). Ill think about the problem tomorrow again... – Jingo Aug 16 '12 at 01:02
  • morning Jingo, just updated the question again. Its tricky in request.POST, shall we have a discussion in the chat? :) – Houman Aug 16 '12 at 11:19
  • 1
    Hey Kave, I am at work atm, I could try to join chat when noone is around :P, the situtation is really tricky, maybe we have to tackle the problem from the beginning again, I got a feeling like that :P – Jingo Aug 16 '12 at 11:51
  • @Kave I just saw your question about the widget replacement for mobile devices... so have you had success in this matter? – Jingo Aug 17 '12 at 14:04
  • @Jingo, I am trying my best. Still no luck. I got my own dropdown-in-formset solution working until request.POST stage. In there it crashes due the way you suggested doing the queryset in the Form. This needs to be done differently. I found out `modelformset_factory` takes a `formfield_callback` paramater. WIth that we should be able to pass in the queryset from there. That would be the final solution. But I don't understand how to use `formfield_callback` in there. If you find any information plz let me know. In the meanwhile I will look into some jquery solutions for this and update you.:) – Houman Aug 17 '12 at 14:41

1 Answers1

0

I hope I understood the goal you want to achieve right. Then maybe you could use ModelForm and its available instance like this:

class SalesItemFSForm(forms.ModelForm):
    class Meta:
        model = SalesItem

    def __init__(self, *args, **kwargs):
        super(SalesItemFSForm, self).__init__(*args, **kwargs)
        self.sale_items = self.instance.company.salesitem_set.all()
        self.fields['sales_item'] = forms.ModelChoiceField(queryset=self.sale_items)

This is untested though and just a thought. I hope this leads into the right direction, but if its totally wrong, let me know and i will remove my answer, so that others wont be confused :).

Jingo
  • 3,200
  • 22
  • 29
  • Interesting. I could try this, but logically it might be problematic, since the salesitem.company.sales_item_set.all() would be the same as SalesItem.objects.all(), no? And thats a problem, because I want to filter it for the given company. :) Not sure...need to test. Thank you though – Houman Aug 15 '12 at 20:05
  • At least in the shell those two queryset are not the same. Could you already test? Id really be interested in the findings :), cause I am trying to solve some similiar questions while trying to programm a small survey-system... – Jingo Aug 15 '12 at 21:00
  • Sure no problem. I have tried it and get this error message when instantiating it: `salesitem_formsets = formset_type(data)` the error message is: `self.sale_items = self.instance.company.salesitem_set.all()` and `raise self.field.rel.to.DoesNotExist`. See full traceback: http://www.heypasteit.com/clip/0FWR – Houman Aug 15 '12 at 21:23
  • hmm ok, you could try to use modelformset_factory and pass the model as first argument. Like so form = modelformset_factory(SalesItem, extra=0, form=SalesItemFSForm) – Jingo Aug 15 '12 at 21:42
  • You are a genius.Its almost fully working. See updated question. :) – Houman Aug 15 '12 at 21:56