Use Forms

django-enum provides custom form fields and widgets that make including EnumField and FlagField fields on forms easy.

Using EnumChoiceField

By default Django’s ModelForm will use ChoiceField to represent EnumField fields. This works great, but if you’re using an enumeration with symmetric properties or with custom _missing_() behavior that you would like the form to respect you must use django-enum’s EnumChoiceField instead. This will happen by default when using ModelForm forms because field construction is delegated to formfield().

Tip

See Django’s documentation on how to customize form fields for more detailed instructions on how to alter the default form behavior.

The form fields can be used directly on any form and we can use the configuration parameters to alter behavior including, modifying the choices list or allowing non-strict values. For example, using our TextChoicesExample - if color_ext was declared with strict=False, we could add additional choices and set the form field to any string like so:

from django.forms import Form
from django_enum.forms import EnumChoiceField


class TextChoicesExampleForm(Form):

    color = EnumChoiceField(TextChoicesExample.Color)

    # since this field is not strict, we can set it to a value not in our
    # enum or choice tuple.
    color_ext = EnumChoiceField(
        TextChoicesExample.Color,
        strict=False,
        choices=[
            ('P', 'Purple'),
            ('O', 'Orange'),
        ] + TextChoicesExample.Color.choices
    )

# when this form is rendered in a template it will include a selected
# option for the value 'Y' that is not part of our Color enumeration.
form = TextChoicesExampleForm(
    initial={
        "color": TextChoicesExample.Color.RED,
        "color_ext": "Y"
    }
)

color_ext will validate any string value, but color will raise a ValidationError if anything other than a valid Color enum is set.

The default widgets used above derive from django.forms.Select. We can also use radio buttons instead by using django.forms.RadioSelect and django_enum.forms.NonStrictRadioSelect:

from django.forms import Form, RadioSelect
from django_enum.forms import EnumChoiceField, NonStrictRadioSelect


class TextChoicesExampleForm(Form):

    color = EnumChoiceField(TextChoicesExample.Color, widget=RadioSelect)
    
    # since this field is not strict, we can set it to a value not in our
    # enum or choice tuple.
    color_ext = EnumChoiceField(
        TextChoicesExample.Color,
        strict=False,
        choices=[
            ('P', 'Purple'),
            ('O', 'Orange'),
        ] + TextChoicesExample.Color.choices,
        widget=NonStrictRadioSelect
    )


# when this form is rendered in a template it will include a selected
# option for the value 'Y' that is not part of our Color enumeration.
form = TextChoicesExampleForm(
    initial={
        "color": TextChoicesExample.Color.RED,
        "color_ext": "Y"
    }
)

Tip

Have a look at widgets to see all the widgets that are available.

Using EnumFlagField

FlagField fields must be rendered with django_enum.forms.EnumFlagField on forms to work properly. This will happen automatically for django.forms.ModelForm because it delegates the field creation to formfield(). This form field type accepts incoming lists of flags and combines them into composite flag types. The cleaned value is a single flag instance value, but the interface to world is decomposed.

This example uses the Permissions enum from the flags howto.

from django.forms import Form
from django_enum.forms import EnumFlagField
from django_enum import utils


class PermissionsExampleForm(Form):

    permissions = EnumFlagField(Group.Permissions)

    # form fields can be non-strict just like model fields.
    # for this field we add an additional flag for delete and
    # set the field to be non-strict
    permissions_ext = EnumFlagField(
        Group.Permissions,
        strict=False,
        choices=[
            *utils.choices(Group.Permissions),
            (1 << 3, "DELETE")
        ]
    )


form = PermissionsExampleForm(
    initial={
        "permissions": Group.Permissions.READ | Group.Permissions.EXECUTE,
        "permissions_ext": Group.Permissions.READ | Group.Permissions.WRITE | (1 << 3)
    }
)

The default widgets used above derive from django.forms.SelectMultiple. We can also use checkboxes instead by using django_enum.forms.FlagCheckbox and django_enum.forms.NonStrictFlagCheckbox:

This example uses the Permissions enum from the flags howto.

from django.forms import Form
from django_enum.forms import EnumFlagField, FlagCheckbox, NonStrictFlagCheckbox
from django_enum import utils


class PermissionsExampleForm(Form):

    permissions = EnumFlagField(Group.Permissions, widget=FlagCheckbox)

    # form fields can be non-strict just like model fields.
    # for this field we add an additional flag for delete and
    # set the field to be non-strict
    permissions_ext = EnumFlagField(
        Group.Permissions,
        strict=False,
        choices=[
            *utils.choices(Group.Permissions),
            (1 << 3, "DELETE")
        ],
        widget=NonStrictFlagCheckbox
    )


form = PermissionsExampleForm(
    initial={
        "permissions": Group.Permissions.READ | Group.Permissions.EXECUTE,
        "permissions_ext": Group.Permissions.READ | Group.Permissions.WRITE | (1 << 3)
    }
)