0

I have the following Django models:

class Contact(models.Model):
    id #primary key
    #Other contact info

class ContactType(models.Model):
    contacttype_choices=(('Primary', 'Primary'),
                         ('Billing', 'Billing'),
                         ('Business', 'Business'),
                         ('Technology', 'Technology'))
    contact=models.ForeignKey(Contact)
    type=models.CharField(choices=contacttype_choices, max_length=30)
    class Meta:
        unique_together=('contact', 'type')

So any contact object can have up to four contact types, with each type either being present or absent. I would like to make a Model Form for Contact with a multiple choice field for contact type. How can I populate this contact type field with the existing values when I construct the Contact form with a Contact instance?

Edit: To clarify, I want one checkbox to be created for each of those four choices, and if the form is instantiated with a model instance, then I want the checkboxes to be checked for each of the related objects that exists, similar to what happens automatically with the rest of the fields.

murgatroid99
  • 19,007
  • 10
  • 60
  • 95
  • Did you get this sentence completed? ... "So any contact object can have up to contact types." – jcfollower Jul 26 '11 at 18:27
  • Does this help you? [Django multiple choice field](http://stackoverflow.com/questions/2726476/django-multiple-choice-field-checkbox-select-multiple) – jcfollower Jul 26 '11 at 19:09
  • Not really. My question is more about populating the form with existing values before it is rendered. – murgatroid99 Jul 26 '11 at 19:13
  • Do you mean to just populate the multiple choice field choices with Primary, Billing (etc), or to have a form generated from a specific model, where the types that the model already has present are pre-selected in the multiple choice field? – waffle paradox Jul 26 '11 at 19:13
  • I clarified my question in my edit. – murgatroid99 Jul 26 '11 at 19:17

2 Answers2

1

I would probably structure the models as such, so I can choose which contact type when creating/editing the Contact. If ContactType is related as a ManyToMany, we automatically get a ModelMultpleChoiceField in the ModelForm, and all we need to do is use the CheckboxSelectMultiple widget to get the output you're looking for.

When we pass an instance of Contact to the ContactForm, Django will bind any pre-selected ContactType choices and check the checkboxes for us.

Setting the title of each ContactType to unique does the same as unique_together on the contact and contact_type.

#models.py
class Contact(models.Model):
    #other fields
    contact_types = models.ManyToMany(ContactType)

class ContactType(models.Model):
    title = models.CharField(max_length=20, unique=true)

#forms.py
class ContactForm(forms.ModelForm):
    class Meta:
        model = Contact

    def __init__(self, *args, **kwargs):
        super(ContactForm, self).__init__(*args, **kwargs)
        self.fields['contact_types'].widget = forms.CheckboxSelectMultiple()
Brandon Taylor
  • 33,823
  • 15
  • 104
  • 144
  • My problem is not with creating the choices (or even with duplicating them between models and views). My problem is that when a model form is instantiated with a model instance, it pre-populates the form fields with the values from that instance, and I want to duplicate that behavior in an additional field that is not directly part of the model. – murgatroid99 Jul 26 '11 at 19:26
  • I'm going to revise my answer because I don't think you're going to be able to do what you want given the current state of the models. – Brandon Taylor Jul 26 '11 at 19:30
  • Yes, @waffle's example works, but I don't feel that the ContactType having a foreign key to Contact is the best way to model that information. My $0.02. – Brandon Taylor Jul 26 '11 at 19:58
1

Have you tried something like this?

class ContactForm(forms.ModelForm):
    class Meta:
        model = Contact
    def __init__(self, *args, **kwargs):
        super(ContactForm, self).__init__(*args, **kwargs)
        self.fields['contacttypes'] = forms.MultipleChoiceField(
            choices = CONTACT_TYPE_CHOICES, 
            label = "contact type",
            widget = CheckBoxSelectMultiple
        )

contact = Contact.objects.get(pk=1) # or whatever
types = ContactType.objects.filter(contact = contact)
form = ContactForm(instance=contact, initial={'contacttypes' : types})
waffle paradox
  • 2,755
  • 18
  • 19
  • This was successful, except that I did not need to define the additional field inside the initializer and I added `.values_list('type', flat=True)` to the end of the second query. I did not think that would work because the docs for `initial` say "they're not used as fallback values if a particular value isn't provided", and I misunderstood it. – murgatroid99 Jul 26 '11 at 19:45
  • I would recommend defining the field with the choices in the init regardless, for the reason you cited above. While it may have worked this time without the explicit definition, I don't think you should rely on that behavior. – waffle paradox Jul 26 '11 at 19:54