views:

2260

answers:

3

ModelMultipleChoiceField doesn't select initial choices and I can't make the following fix (link below) work in my example:

http://code.djangoproject.com/ticket/5247#comment:6

My models and form:

class Company(models.Model):
    company_name = models.CharField(max_length=200)

class Contact(models.Model):
    company = models.ForeignKey(Company)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

class Action(models.Model):
    company = models.ForeignKey(Company, blank=True, null=True)
    from_company = models.ManyToManyField(Contact, verbose_name='Participant(s) from "Company"', blank=True, null=True)

class Action_Form(ModelForm):
    from_company = forms.ModelMultipleChoiceField(queryset=Contact.objects.none(), widget=forms.CheckboxSelectMultiple())
    class Meta:
        model = Action

What I do and the results:

>>> contacts_from_company = Contact.objects.filter(company__exact=1) # "1" for test, otherwise a variable
>>> form = Action_Form(initial={'from_company': [o.pk for o in contacts_from_company]}) # as suggested in the fix
>>> print form['from_company']
<ul>
</ul>
>>> print contacts_from_company
[<Contact: test person>, <Contact: another person>]

>>> form2 = Action_Form(initial={'from_company': contacts_from_company})
>>> print form2['from_company']
<ul>
</ul>

>>> form3 = Action_Form(initial={'from_company': Contact.objects.all()})
>>> print form3['from_company']
<ul>
</ul>

The way I was hoping it would work:
1. My view gets "company" from request.GET
2. It then filters all "contacts" for that "company"
3. Finally, it creates a form and passes those "contacts" as "initial={...}"

Two questions:
1. [not answered yet] How can I make ModelMultipleChoiceField take those "initial" values?
2. [answered] As an alternative, can I pass a variable to Action_Form(ModelForm) so that in my ModelForm I could have:

from_company = forms.ModelMultipleChoiceField(queryset=Contact.objects.filter(company__exact=some_id) # where some_id comes from a view
+5  A: 

You will need to add an __init__ method to Action_Form to set your initial values, remembering to call __init__ on the base ModelForm class via super. See this blog post for details.

class Action_Form(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(Action_Form, self).__init__(*args, **kwargs)
        self.fields['from_company'].queryset = Contact.object.filter(...

If you plan to pass your filter params as keyword args to Action_Form, you'll need to remove them prior invoking super:

myfilter = kwargs['myfilter']
del kwargs['myfilter']

or, probably better:

myfilter = kwargs.pop('myfilter')

For more information, here's another link referring to Dynamic ModelForms in Django.

Jeff Bauer
Thanks! I tried the __init__ method but I don't know how to pass a variable to it, or to ModelForm. My form does not have initial values as it is the view that is supposed to pass them.Could you please explain?
dmi
Thank you so much! This solves my problem, I can pass filter params now. I wonder if they will patch the initial values issue any time soon though.
dmi
And thank you for the link to a very helpful article!
dmi
+4  A: 
Andre Miras
You helped me a lot with that comment from the Django source code.
Török Gábor
A: 

If previous answer wasn't straight-forward enough, I try to answer 1) again:

  1. How can I make ModelMultipleChoiceField take those "initial" values?

You can left Action_Form as it was in the original question, and just use this to render exactly what you want:

>>> form4 = Action_Form(initial={'from_company': Contact.objects.all().values_list('id',flat=True)})
>>> print form4['from_company']
ygneo