[Django]-Django Multiple Choice Field / Checkbox Select Multiple

40๐Ÿ‘

โœ…

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

19๐Ÿ‘

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))
๐Ÿ‘คtwampss

13๐Ÿ‘

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.

๐Ÿ‘คWebthusiast

3๐Ÿ‘

ManyToManyField isn`t a good choice.You can use some snippets to implement MultipleChoiceField.You can be inspired by MultiSelectField with comma separated values (Field + FormField)
But it has some bug in it.And you can install django-multiselectfield.This is more prefect.

๐Ÿ‘คwyx

1๐Ÿ‘

Iโ€™ve implemented a very simple form field that converts from and to a CharField:

from django.forms import ModelForm, MultipleChoiceField, ValidationError, CheckboxSelectMultiple
from spellbook.models import MyModel


class CheckboxSelectMultipleAsCharField(CheckboxSelectMultiple):
    def format_value(self, value):
        if value is not None and isinstance(value, str):
            value = list(value)
        return super().format_value(value)


class MultipleChoiceFieldAsCharField(MultipleChoiceField):
    widget = CheckboxSelectMultipleAsCharField
    def to_python(self, value):
        return ''.join(super().to_python(value))

    def validate(self, value):
        super().validate(value)
        if len(value) > len(self.choices):
            raise ValidationError('Too many choices.')



class MyModelForm(ModelForm):
    my_char_field = MultipleChoiceFieldAsCharField(choices=MyModel.OptionsEnum.choices, required=True)
    ...
๐Ÿ‘คDeloo

0๐Ÿ‘

The easiest way I found (just I use eval() to convert string gotten from input to tuple to read again for form instance or other place)

This trick works very well

#model.py
class ClassName(models.Model):
    field_name = models.CharField(max_length=100)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.field_name:
            self.field_name= eval(self.field_name)



#form.py
CHOICES = [('pi', 'PI'), ('ci', 'CI')]

class ClassNameForm(forms.ModelForm):
    field_name = forms.MultipleChoiceField(choices=CHOICES)

    class Meta:
        model = ClassName
        fields = ['field_name',]
๐Ÿ‘คyounesfmgtc

0๐Ÿ‘

You can easily achieve this using ArrayField:

# in my models...
tags = ArrayField(models.CharField(null=True, blank=True, max_length=100, choices=SECTORS_TAGS_CHOICES), blank=True, default=list)

# in my forms...
class MyForm(forms.ModelForm):

    class Meta:
        model = ModelClass
        fields = [..., 'tags', ...]

I use tagsinput JS library to render my tags but you can use whatever you like:
This my template for this widget:

{% if not hidelabel and field.label %}<label for="{{ field.id_for_label }}">{{ field.label }}</label>{% endif %}
<input id="{{ field.id_for_label }}" type="text" name="{{ field.name }}" data-provide="tagsinput"{% if field.value %} value="{{ field.value }}"{% endif %}{% if field.field.disabled %} disabled{% endif %}>
{% if field.help_text %}<small id="{{ field.name }}-help-text" class="form-text text-muted">{{ field.help_text | safe }}</small>{% endif %}
๐Ÿ‘คjuanifioren

Leave a comment