A custom Django field able to use subclasses of Python's internal `Enum` class as choices
Project description
django-enum-choices
A custom Django choice field to use with Python enums.
Table of Contents
- Installation
- Basic Usage
- Postgres ArrayField Usage
- Usage with Django Rest Framework
- Serializing PostgreSQL ArrayField
- Customizing readable values
- Implementation details
- Using Python's
enum.auto - Development
Installation
pip install django-enum-choices
Basic Usage
from django.db import models
from django_enum_choices.fields import EnumChoiceField
from enum import Enum
class MyEnum(Enum):
A = 'a'
B = 'b'
class MyModel(models.Model):
enumerated_field = EnumChoiceField(MyEnum)
Model creation
instance = MyModel.objects.create(enumerated_field=MyEnum.A)
Changing enum values
instance.enumerated_field = MyEnum.B
instance.save()
Filtering
MyModel.objects.filter(enumerated_field=MyEnum.A)
Postgres ArrayField Usage
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django_enum_choices.fields import EnumChoiceField
from enum import Enum
class MyEnum(Enum):
A = 'a'
B = 'b'
class MyModelMultiple(models.Model):
enumerated_field = ArrayField(
base_field=EnumChoiceField(MyEnum)
)
Model Creation
instance = MyModelMultiple.objects.create(enumerated_field=[MyEnum.A, MyEnum.B])
Changing enum values
instance.enumerated_field = [MyEnum.B]
instance.save()
Usage with Django Rest Framework
Using a subclass of serializers.Serializer
from rest_framework import serializers
from django_enum_choices.serializers import EnumChoiceField
class MySerializer(serializers.Serializer):
enumerated_field = EnumChoiceField(MyEnum)
# Serialization:
serializer = MySerializer({
'enumerated_field': MyEnum.A
})
data = serializer.data # {'enumerated_field': 'a'}
# Deserialization:
serializer = MySerializer(data={
'enumerated_field': 'a'
})
serializer.is_valid()
data = serializer.validated_data # OrderedDict([('enumerated_field', <MyEnum.A: 'a'>)])
Using a subclass of serializers.ModelSerializer
from rest_framework import serializers
from django_enum_choices.serializers import EnumChoiceField
class MyModelSerializer(serializers.ModelSerializer):
enumerated_field = EnumChoiceField(MyEnum)
class Meta:
model = MyModel
fields = ('enumerated_field', )
# Serialization:
instance = MyModel.objects.create(enumerated_field=MyEnum.A)
serializer = MyModelSerializer(instance)
data = serializer.data # {'enumerated_field': 'a'}
# Saving:
serializer = MyModelSerializer(data={
'enumerated_field': 'a'
})
serializer.is_valid()
serializer.save()
Caveat
If you don't explicitly specify the enumerated_field = EnumChoiceField(MyEnum), then you need to include the EnumChoiceModelSerializerMixin:
from rest_framework import serializers
from django_enum_choices.serializers import EnumChoiceModelSerializerMixin
class ImplicitMyModelSerializer(
EnumChoiceModelSerializerMixin,
serializers.ModelSerializer
):
class Meta:
model = MyModel
fields = ('enumerated_field', )
By default ModelSerializer.build_standard_field coerces any field that has a model field with choices to ChoiceField which returns the value directly.
Since enum values resemble EnumClass.ENUM_INSTANCE they won't be able to be encoded by the JSONEncoder when being passed to a Response.
That's why we need the mixin.
Serializing PostgreSQL ArrayField
django-enum-choices exposes a MultipleEnumChoiceField that can be used for serializing arrays of enumerations.
Using a subclass of serializers.Serializer
from rest_framework import serializers
from django_enum_choices.serializers import MultipleEnumChoiceField
class MultipleMySerializer(serializers.Serializer):
enumerated_field = MultipleEnumChoiceField(MyEnum)
# Serialization:
serializer = MultipleMySerializer({
'enumerated_field': [MyEnum.A, MyEnum.B]
})
data = serializer.data # {'enumerated_field': ['a', 'b']}
# Deserialization:
serializer = MultipleMySerializer(data={
'enumerated_field': ['a', 'b']
})
serializer.is_valid()
data = serializer.validated_data # OrderedDict([('enumerated_field', [<MyEnum.A: 'a'>, <MyEnum.B: 'b'>])])
Using a subclass of serializers.ModelSerializer
class ImplicitMultipleMyModelSerializer(
EnumChoiceModelSerializerMixin,
serializers.ModelSerializer
):
class Meta:
model = MyModelMultiple
fields = ('enumerated_field', )
# Serialization:
instance = MyModelMultiple.objects.create(enumerated_field=[MyEnum.A, MyEnum.B])
serializer = ImplicitMultipleMyModelSerializer(instance)
data = serializer.data # {'enumerated_field': ['a', 'b']}
# Saving:
serializer = ImplicitMultipleMyModelSerializer(data={
'enumerated_field': ['a', 'b']
})
serializer.is_valid()
serializer.save()
The EnumChoiceModelSerializerMixin does not need to be used if enumerated_field is defined on the serializer class explicitly.
Customizing readable values
If a get_readable_value method is provided, django_enum_choices will use it to produce the readable values that are written in the database:
class CustomReadableValueEnum(Enum):
A = 'a'
B = 'b'
@classmethod
def get_readable_value(cls, choice):
return cls(choice).value.upper()
Using the above class as an enum_class argument to django_enum_choices.fields.EnumChoiceField will produce the choices for the database as (('a', 'A'), ('b', 'B'))
Implementation details
EnumChoiceFieldis a subclass ofCharField.- Only subclasses of
Enumare valid arguments forEnumChoiceField. choicesare generated from the values of the enumeration, like(str(value), str(value)), meaning you can put any valid Python object there.max_length, if passed, is ignored.max_lengthis automatically calculated from the longest choice.
For example, lets have the following case:
class Value:
def __init__(self, value):
self.value = value
def __str__(self):
return self.value
class CustomObjectEnum(Enum):
A = Value(1)
B = Value('B')
class SomeModel(models.Model):
enumerated_field = EnumChoiceField(CustomObjectEnum)
We'll have the following:
SomeModel.enumerated_field.choices == (('1', '1'), ('B', 'B'))SomeModel.enumerated_field.max_length == 3
Using Python's enum.auto
enum.auto can be used for shorthand enumeration definitions:
from enum import Enum, auto
class AutoEnum(Enum):
A = auto() # 1
B = auto() # 2
class SomeModel(models.Model):
enumerated_field = EnumChoiceField(Enum)
This will result in the following:
SomeModel.enumerated_field.choices == (('1', '1'), ('2', '2'))
Overridinng auto behaviour
Custom values for enumerations, created by auto, can be defined by
subclassing an Enum that defines _generate_next_value_:
class CustomAutoEnumValueGenerator(Enum):
def _generate_next_value_(name, start, count, last_values):
return {
'A': 'foo',
'B': 'bar'
}[name]
class CustomAutoEnum(CustomAutoEnumValueGenerator):
A = auto()
B = auto()
The above will assign the values mapped in the dictionary as values to attributes in CustomAutoEnum.
Development
Prerequisites
- SQLite3
- PostgreSQL server
- Python >= 3.5 virtual environment
git clone https://github.com/HackSoftware/django-enum-choices.git
cd django_enum_choices
pip install -e .[dev]
Linting and running the tests:
tox
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_enum_choices-1.0.2.tar.gz.
File metadata
- Download URL: django_enum_choices-1.0.2.tar.gz
- Upload date:
- Size: 17.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.6.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8496e4c4fc589d53ae82ad2fea15599a7b97c60bdd2304f8136a814d58743087
|
|
| MD5 |
23506dc0f2b403f6ecc9b2e99a0ea239
|
|
| BLAKE2b-256 |
82dc622efed0e0f9d75afcc2fc13c9b353357fffd6af385190f1016b2621d75d
|
File details
Details for the file django_enum_choices-1.0.2-py2.py3-none-any.whl.
File metadata
- Download URL: django_enum_choices-1.0.2-py2.py3-none-any.whl
- Upload date:
- Size: 20.2 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.6.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5634f51971a0f7640f87e725acc3bd8ada18ded47a4ca449acdb0e2c15bedb27
|
|
| MD5 |
a24ad7aa4aff72dc8f4241afd5620803
|
|
| BLAKE2b-256 |
2c061a50d3557c94a88d9a6f85e92976d8f01ac3d019bc581a7c296dae5c00d4
|