Integrate with …¶
Tip
EnumField
instances ultimately inherit from existing core Django
model fields and set the choices attribute. Therefore
EnumField
should work with any third party libraries and will
behave as a core field with a defined choice tuple list would.
However, you may want to take advantage of some of the extra features provided by django-enum. We provide out-of-the-box integration with the following libraries:
enum-properties¶
Almost any enum.Enum
type is supported, so you may make use of enum.Enum
extension libraries like enum-properties to define very rich
enumeration fields. You will need to install the properties optional dependency set:
pip install "django-enum[properties]"
enum-properties is an extension to enum.Enum
that allows
properties to be added to enumeration instances using a simple declarative syntax. This is a less
awkward and more compatible alternative to dataclass enumerations
.
If you find yourself considering a dataclass enumeration
, consider using
enum-properties instead. Dataclass value types do not work with
EnumField
. Most libraries that work with enumerations expect the
value
attribute to be a primitive serializable type.
enum-properties also allows for symmetric properties which compare as equivalent to the enumeration values and can be used to instantiate enumeration instances.
For a real-world example see the properties tutorial.
It should be unnecessary, but if you need to integrate with code that expects an interface fully
compatible with Django’s
enumeration types
(TextChoices
and IntegerChoices
django-enum provides
TextChoices
, IntegerChoices
,
FlagChoices
and FloatChoices
types that
derive from enum-properties and Django’s Choices
. For instance,
you may be using a third party library that uses isinstance()
checks on your enum types
instead of duck typing. For compatibility in these cases simply use django-enum’s Choices
types
as the base class for your enumeration instead:
import typing as t
from django.db import models
from typing_extensions import Annotated
from django_enum import EnumField
from django_enum.choices import TextChoices
from enum_properties import Symmetric, symmetric
class TextChoicesExample(models.Model):
class Color(TextChoices):
# no need to specify label because it is built in
rgb: Annotated[t.Tuple[int, int, int], Symmetric()]
hex: Annotated[str, Symmetric(case_fold=True)]
# fmt: off
# name value label rgb hex
RED = "R", "Red", (1, 0, 0), "ff0000"
GREEN = "G", "Green", (0, 1, 0), "00ff00"
BLUE = "B", "Blue", (0, 0, 1), "0000ff"
# fmt: on
# by default label is symmetric, but case sensitive
# to make it case-insensitive we can override the property
# and mark it like this:
@symmetric(case_fold=True)
@property
def label(self) -> str:
return self._label_
color = EnumField(Color)
All of the expected enum-properties behavior works:
obj = TextChoicesExample.objects.create(color=TextChoicesExample.Color.RED)
assert obj.color is TextChoicesExample.Color.RED
assert obj.color.label == 'Red'
assert obj.color.rgb == (1, 0, 0)
assert obj.color.hex == 'ff0000'
# enum-properties symmetric properties work as expected
assert obj.color == 'Red'
assert obj.color == (1, 0, 0)
assert obj.color == 'ff0000'
Note
To make your non-choices derived enum quack like one, you will need to add:
a
choices
property that returns the choices tuple lista
label
property that returns the list of labelsa
name
property that returns the list of namesa
value
property that returns the list of values
Django Rest Framework¶
By default DRF ModelSerializer will use a
ChoiceField to represent an
EnumField
. This works great, but it will not accept symmetric
enumeration values. A serializer field
django_enum.drf.EnumField
is provided that will. FlagField
fields do not work well with DRF’s builtin
MultipleChoiceField
so we also provide a django_enum.drf.FlagField
.
The dependency on DRF is optional so to use the provided serializer field you must install DRF:
pip install djangorestframework
from django_enum.drf import EnumField, FlagField
from rest_framework import serializers
class ExampleSerializer(serializers.Serializer):
color = EnumField(TextChoicesExample.Color)
# from the flags tutorial
constellation = FlagField(Constellation)
ser = ExampleSerializer(
data={
'color': (1, 0, 0),
'constellation': [
Constellation.GALILEO.name,
Constellation.GPS.name
]
}
)
assert ser.is_valid()
The serializer django_enum.drf.EnumField
accepts any arguments that
ChoiceField does. It also
accepts the strict
parameter which behaves the same way as it does on the model field.
Tip
You only need to use django_enum.drf.EnumField
if:
You are integrating with enum-properties and want symmetric properties to work.
You have non-strict model fields and want to allow your API to accept values outside of the enumeration.
The django_enum.drf.EnumField
must be used for any FlagField
fields. It will accept a composite integer or a list of any values coercible to a flag. The
serialized output will be an composite integer holding the full bitfield.
ModelSerializers¶
An django_enum.drf.EnumFieldMixin
class is provided that when added to
ModelSerializers
will be sure that the serializer instantiates the correct django-enum serializer field type:
from django_enum.drf import EnumFieldMixin
from rest_framework import serializers
class ExampleModelSerializer(EnumFieldMixin, serializers.Serializer):
class Meta:
model = TextChoicesExample
ser = ExampleModelSerializer(
data={
'color': (1, 0, 0),
}
)
assert ser.is_valid()
django-filter¶
As shown above, filtering by any value, enumeration type instance or symmetric value works with
Django’s ORM. This is not natively true for the default
django_filters.filterset.FilterSet
from django-filter.
Those filter sets will only be filterable by direct enumeration value by default. An
EnumFilter
class is provided to enable filtering by symmetric property
values, but since the dependency on django-filter is optional, you must
first install it:
pip install django-filter
from django_enum.filters import EnumFilter
from django_filters.views import FilterView
from django_filters import FilterSet
class TextChoicesExampleFilterViewSet(FilterView):
class TextChoicesExampleFilter(FilterSet):
color = EnumFilter(enum=TextChoicesExample.Color)
class Meta:
model = TextChoicesExample
fields = '__all__'
filterset_class = TextChoicesExampleFilter
model = TextChoicesExample
# now filtering by symmetric value in url parameters works:
# e.g.: /?color=FF0000
A FilterSet
class is also provided that uses
EnumFilter
for EnumField
by default.
So the above is also equivalent to:
from django_filters.views import FilterView
class TextChoicesExampleFilterViewSet(FilterView):
class TextChoicesExampleFilter(EnumFilterSet):
class Meta:
model = TextChoicesExample
fields = '__all__'
filterset_class = TextChoicesExampleFilter
model = TextChoicesExample
Tip
FilterSet
may also be used as a mixin.
FlagFields¶
An EnumFlagFilter
field for flag fields is also provided:
from django_enum.filters import EnumFlagFilter
from django_filters.views import FilterView
from django_filters import FilterSet
class FlagExampleFilterViewSet(FilterView):
class FlagExampleFilter(FilterSet):
constellation = EnumFlagFilter(enum=Constellation)
class Meta:
model = GNSSReceiver
fields = '__all__'
filterset_class = FlagExampleFilter
model = GNSSReceiver
# now filtering by flags works:
# e.g.: /?constellation=GPS&constellation=GLONASS