There’s a lot of times you want to have a multiple choice set of flags and using a many-to-many database relation is massively overkill. Django 1.9 added a builtin modelfield to leverage Postgres’ built-in array support. Unfortunately the default formfield for ArrayField, SimpleArrayField, is not even a little bit useful (it’s a comma-delimited text input).
If you’re writing your own form, you can simply use the MultipleChoiceField formfield, but if you’re using something that builds forms using the ModelForm automagic factories with no overrides (e.g. Wagtail’s admin site), you need a way to specify the formfield.
Instead subclass ArrayField:
from django import forms from django.contrib.postgres.fields import ArrayField class ChoiceArrayField(ArrayField): """ A field that allows us to store an array of choices. Uses Django 1.9's postgres ArrayField and a MultipleChoiceField for its formfield. """ def formfield(self, **kwargs): defaults = { 'form_class': forms.MultipleChoiceField, 'choices': self.base_field.choices, } defaults.update(kwargs) # Skip our parent's formfield implementation completely as we don't # care for it. # pylint:disable=bad-super-call return super(ArrayField, self).formfield(**defaults)
You use this like ArrayField, except that choices is required.
FLAG_CHOICES = ( ('defect', 'Defect'), ('enhancement', 'Enhancement'), ) flags = ChoiceArrayField(models.CharField(max_length=..., choices=FLAG_CHOICES), default=['defect'])
You can similarly use this with any other field that supports choices, e.g. IntegerField (but you’re not storing choices as integers… are you).
Gist.