Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: ModelMultipleChoiceField doesn't select initial choices

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
like image 681
dmi Avatar asked Jan 28 '09 15:01

dmi


4 Answers

I'm replying for 1)

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

This could be done in your Action_Form __init__ method using ModelMultipleChoiceField initial attribute.

As it says in the Django source code (db/models/fields/related.py) in def formfield(self, **kwargs):

        # If initial is passed in, it's a list of related objects, but the
        # MultipleChoiceField takes a list of IDs.

So you need to give it a list of IDs:

class Action_Form(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(Action_Form, self).__init__(*args, **kwargs)
        self.fields['from_company'].initial = [c.pk for c in Contact.object.filter()]
like image 193
Andre Miras Avatar answered Oct 23 '22 06:10

Andre Miras


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.

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.

like image 22
Jeff Bauer Avatar answered Oct 23 '22 07:10

Jeff Bauer


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 leave 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']
like image 13
ygneo Avatar answered Oct 23 '22 07:10

ygneo


Answer to (1) question!

This will not work:

self.fields['from_company'].initial = [c.pk for c in Contact.object.filter()]

But this will really work:

self.initial['from_company'] = [c.pk for c in Contact.object.filter()]
like image 12
Albert Tugushev Avatar answered Oct 23 '22 07:10

Albert Tugushev