Django Multiple Choice Field / Checkbox Select Multiple

I have a Django application and want to display multiple choice checkboxes in a user's profile. They will then be able to select multiple items.

This is a simplified version of my models.py:

from profiles.choices import SAMPLE_CHOICES

class Profile(models.Model):
    user = models.ForeignKey(User, unique=True, verbose_name_('user'))
    choice_field = models.CharField(_('Some choices...'), choices=SAMPLE_CHOICES, max_length=50)

And my form class:

class ProfileForm(forms.ModelForm):
    choice_field = forms.MultipleChoiceField(choices=SAMPLE_CHOICES, widget=forms.CheckboxSelectMultiple)

    class Meta:
        model = Profile

And my views.py:

if request.method == "POST":
    profile_form = form_class(request.POST, instance=profile)
    if profile_form.is_valid():
        ...
        profile.save()
return render_to_response(template_name, {"profile_form": profile_form,}, context_instance=RequestContext(request))

I can see that the POST is only sending one value:

choice_field u'choice_three' 

And the local vars params is sending a list:

[u'choice_one', u'choice_two', u'choice_three']

All of the form fields display correct, but when I submit a POST, I get an error

Error binding parameter 7 - probably unsupported type.

Do I need to process the multiple choice field further in the view? Is the model field type correct? Any help or references would be greatly appreciated.


The profile choices need to be setup as a ManyToManyField for this to work correctly.

So... your model should be like this:

class Choices(models.Model):
  description = models.CharField(max_length=300)

class Profile(models.Model):
  user = models.ForeignKey(User, blank=True, unique=True, verbose_name='user')
  choices = models.ManyToManyField(Choices)

Then, sync the database and load up Choices with the various options you want available.

Now, the ModelForm will build itself...

class ProfileForm(forms.ModelForm):
  Meta:
    model = Profile
    exclude = ['user']

And finally, the view:

if request.method=='POST':
  form = ProfileForm(request.POST)
  if form.is_valid():
    profile = form.save(commit=False)
    profile.user = request.user
    profile.save()
else:
  form = ProfileForm()

return render_to_response(template_name, {"profile_form": form}, context_instance=RequestContext(request))

It should be mentioned that you could setup a profile in a couple different ways, including inheritance. That said, this should work for you as well.

Good luck.


Brant's solution is absolutely correct, but I needed to modify it to make it work with multiple select checkboxes and commit=false. Here is my solution:

models.py

class Choices(models.Model):
    description = models.CharField(max_length=300)

class Profile(models.Model):
   user = models.ForeignKey(User, blank=True, unique=True, verbose_name_('user'))
   the_choices = models.ManyToManyField(Choices)

forms.py

class ProfileForm(forms.ModelForm):
    the_choices = forms.ModelMultipleChoiceField(queryset=Choices.objects.all(), required=False, widget=forms.CheckboxSelectMultiple)

    class Meta:
        model = Profile
        exclude = ['user']

views.py

if request.method=='POST':
    form = ProfileForm(request.POST)
    if form.is_valid():
        profile = form.save(commit=False)
        profile.user = request.user
        profile.save()
        form.save_m2m() # needed since using commit=False
    else:
        form = ProfileForm()

return render_to_response(template_name, {"profile_form": form}, context_instance=RequestContext(request))

The models.CharField is a CharField representation of one of the choices. What you want is a set of choices. This doesn't seem to be implemented in django (yet).

You could use a many to many field for it, but that has the disadvantage that the choices have to be put in a database. If you want to use hard coded choices, this is probably not what you want.

There is a django snippet at http://djangosnippets.org/snippets/1200/ that does seem to solve your problem, by implementing a ModelField MultipleChoiceField.