summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-04-11 07:21:24 +0000
committerCoprDistGit <infra@openeuler.org>2023-04-11 07:21:24 +0000
commit2b71872b92f6dcfe02f452c71e8b4c0a126f0a27 (patch)
treea9984409135d90a9b6d589d30203af1d84261e41
parent033eb75eb31c5bc8d214f685c3c22a7fef0f3986 (diff)
automatic import of python-django-enum-choices
-rw-r--r--.gitignore1
-rw-r--r--python-django-enum-choices.spec2494
-rw-r--r--sources1
3 files changed, 2496 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..6a100a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/django_enum_choices-2.1.4.tar.gz
diff --git a/python-django-enum-choices.spec b/python-django-enum-choices.spec
new file mode 100644
index 0000000..cda49d8
--- /dev/null
+++ b/python-django-enum-choices.spec
@@ -0,0 +1,2494 @@
+%global _empty_manifest_terminate_build 0
+Name: python-django-enum-choices
+Version: 2.1.4
+Release: 1
+Summary: A custom Django field able to use subclasses of Python's internal `Enum` class as choices
+License: MIT
+URL: https://github.com/HackSoftware/django-enum-choices
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/b2/2c/c1d3fd4469cdc81775f4d466147df6f4e99bc3fa9be24abec1fe1f156b5e/django_enum_choices-2.1.4.tar.gz
+BuildArch: noarch
+
+Requires: python3-Django
+Requires: python3-Django
+Requires: python3-djangorestframework
+Requires: python3-psycopg2
+Requires: python3-flake8
+Requires: python3-pytest
+Requires: python3-pytest-django
+Requires: python3-pytest-pythonpath
+Requires: python3-django-environ
+Requires: python3-tox
+Requires: python3-bumpversion
+Requires: python3-tox-pyenv
+Requires: python3-django-filter
+
+%description
+
+# django-enum-choices (DEPRECATED)
+
+A custom Django choice field to use with [Python enums.](https://docs.python.org/3/library/enum.html)
+
+[![PyPI version](https://badge.fury.io/py/django-enum-choices.svg)](https://badge.fury.io/py/django-enum-choices)
+
+## ⚠️ Disclaimer ⚠️
+
+Starting with version 3.0, Django started supporting [Enumerations for model field choices](https://docs.djangoproject.com/en/dev/releases/3.0/#enumerations-for-model-field-choices) and we recommend using this as a native Django feature, instead of `django-enum-choices`
+
+**If you are using `django-enum-choices` and you want to upgrade your project to Django >= `3.0` you can refer to the guide in the wiki: [Migrating to Django 3](https://github.com/HackSoftware/django-enum-choices/wiki/Migrating-to-Django-3)**
+
+## Table of Contents
+
+- [django-enum-choices](#django-enum-choices)
+ - [Table of Contents](#table-of-contents)
+ - [Installation](#installation)
+ - [Basic Usage](#basic-usage)
+ - [Choice builders](#choice-builders)
+ - [Changing/Removing options from enumerations](#changingremoving-options-from-enumerations)
+ - [Changing options](#changing-options)
+ - [Removing options](#removing-options)
+ - [Usage inside the admin panel](#usage-in-the-admin-panel)
+ - [Usage with forms](#usage-with-forms)
+ - [Usage with `django.forms.ModelForm`](#usage-with-djangoformsmodelform)
+ - [Usage with `django.forms.Form`](#usage-with-djangoformsform)
+ - [Usage with `django-filter`](#usage-with-django-filter)
+ - [By using a `Meta` inner class and inheriting from `EnumChoiceFilterMixin`](#by-using-a-meta-inner-class-and-inheriting-from-enumchoicefiltermixin)
+ - [By declaring the field explicitly on the `FilterSet`](#by-declaring-the-field-explicitly-on-the-filterset)
+ - [Postgres ArrayField Usage](#postgres-arrayfield-usage)
+ - [Usage with Django Rest Framework](#usage-with-django-rest-framework)
+ - [Using `serializers.ModelSerializer` with `EnumChoiceModelSerializerMixin`](#using-serializersmodelserializer-with-enumchoicemodelserializermixin)
+ - [Using `serializers.ModelSerializer` without `EnumChoiceModelSerializerMixin`](#using-serializersmodelserializer-without-enumchoicemodelserializermixin)
+ - [Using a subclass of `serializers.Serializer`](#using-a-subclass-of-serializersserializer)
+ - [Serializing PostgreSQL ArrayField](#serializing-postgresql-arrayfield)
+ - [Implementation details](#implementation-details)
+ - [Using Python's `enum.auto`](#using-pythons-enumauto)
+ - [Development](#development)
+
+## Installation
+
+```bash
+pip install django-enum-choices
+```
+
+## Basic Usage
+
+```python
+from enum import Enum
+
+from django.db import models
+
+from django_enum_choices.fields import EnumChoiceField
+
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+**Model creation**
+
+```python
+instance = MyModel.objects.create(enumerated_field=MyEnum.A)
+```
+
+**Changing enum values**
+
+```python
+instance.enumerated_field = MyEnum.B
+instance.save()
+```
+
+**Filtering**
+
+```python
+MyModel.objects.filter(enumerated_field=MyEnum.A)
+```
+
+## Choice builders
+
+`EnumChoiceField` extends `CharField` and generates choices internally. Each choice is generated using something, called a `choice_builder`.
+
+A choice builder function looks like that:
+
+```python
+def choice_builder(enum: Enum) -> Tuple[str, str]:
+ # Some implementation
+```
+
+If a `choice_builder` argument is passed to a model's `EnumChoiceField`, `django_enum_choices` will use it to generate the choices.
+The `choice_builder` must be a callable that accepts an enumeration choice and returns a tuple,
+containing the value to be saved and the readable value.
+
+By default `django_enum_choices` uses one of the four choice builders defined in `django_enum_choices.choice_builders`, named `value_value`.
+
+It returns a tuple containing the enumeration's value twice:
+
+```python
+from django_enum_choices.choice_builders import value_value
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+print(value_value(MyEnum.A)) # ('a', 'a')
+```
+
+You can use one of the four default ones that fits your needs:
+
+* `value_value`
+* `attribute_value`
+* `value_attribute`
+* `attribute_attribute`
+
+For example:
+
+```python
+from django_enum_choices.choice_builders import attribute_value
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+class CustomReadableValueEnumModel(models.Model):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=attribute_value
+ )
+```
+
+The resulting choices for `enumerated_field` will be `(('A', 'a'), ('B', 'b'))`
+
+You can also define your own choice builder:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+def choice_builder(choice: Enum) -> Tuple[str, str]:
+ return choice.value, choice.value.upper() + choice.value
+
+class CustomReadableValueEnumModel(models.Model):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=choice_builder
+ )
+```
+
+Which will result in the following choices `(('a', 'Aa'), ('b', 'Bb'))`
+
+The values in the returned from `choice_builder` tuple will be cast to strings before being used.
+
+## Changing/Removing options from enumerations
+At any given point of time all instances of a model that has `EnumChoiceField` must have a value that is currently present in the enumeration.
+When changing or removing an option from the enumeration, a custom database migration must be made prior to the enumeration change.
+
+### Changing options
+When chaging options we'll need several operations:
+
+1. Inserting a new option with the new value that we want
+2. Migrating all instances from the old option to the new one
+3. Removing the old option and renaming the old one
+4. Removing the custom data migration code, so migrations can be run on a clean database without an `AttributeError` ocurring
+
+Example:
+
+Initial setup:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+# Desired change:
+# A = 'a_updated'
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+1. Insert a new option with the desired new value:
+```python
+class MyEnum:
+ A_UPDATED = 'a_updated'
+ A = 'a'
+ B = 'b'
+```
+```bash
+python manage.py makemigrations
+```
+
+2. Migrate model instances
+```bash
+python manage.py makemigrations app_label --empty
+```
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ MyModel = apps.get_model('app_label', 'MyModel')
+
+ MyModel.objects.filter(enumerated_field=MyEnum.A).update(enumerated_field=MyEnum.A_UPDATED)
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+```bash
+python manage.py migrate
+```
+
+3. Remove old option and rename new one
+```python
+class MyEnum:
+ A = 'a_updated'
+ B = 'b'
+```
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+4. Remove custom data migration code
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ pass
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+
+### Removing options
+Removing options from the enumeration includes several operations as well:
+
+1. Optional: Making the field nullable (if we want our existing instances' values to be `None`)
+2. Migrating all instances to a new option (or None)
+3. Removing the option from the enumeration
+4. Removing the custom data migration code, so migrations can be run on a clean database without an `AttributeError` ocurring
+
+Example:
+
+Initial setup:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+# Desired change:
+# class MyEnum(Enum):
+# A = 'a'
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+1. Optional: Make the field nullable (if you want your existing instances to have a `None` value)
+```python
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum, blank=True, null=True)
+```
+```bash
+python manage.py makemigrations
+```
+
+2. Migrate model instances
+```bash
+python manage.py makemigrations app_label --empty
+```
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ MyModel = apps.get_model('app_label', 'MyModel')
+
+ MyModel.objects.filter(enumerated_field=MyEnum.B).update(enumerated_field=MyEnum.A)
+ # OR MyModel.objects.filter(enumerated_field=MyEnum.B).update(enumerated_field=None)
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+```bash
+python manage.py migrate
+```
+
+3. Remove old option
+```python
+class MyEnum:
+ A = 'a
+```
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+4. Remove custom data migration code
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ pass
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+
+
+## Usage in the admin panel
+
+Model fields, defined as `EnumChoiceField` can be used with almost all of the admin panel's
+standard functionallities.
+
+One exception from this their usage in `list_filter`.
+
+If you need an `EnumChoiceField` inside a `ModelAdmin`'s `list_filter`, you can use the following
+options:
+
+* Define the entry insite the list filter as a tuple, containing the field's name and `django_enum_choices.admin.EnumChoiceListFilter`
+
+```python
+from django.contrib import admin
+
+from django_enum_choices.admin import EnumChoiceListFilter
+
+from .models import MyModel
+
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ list_filter = [('enumerated_field', EnumChoiceListFilter)]
+```
+
+* Set `DJANGO_ENUM_CHOICES_REGISTER_LIST_FILTER` inside your settings to `True`, which will automatically set the `EnumChoiceListFilter` class to all
+`list_filter` fields that are instances of `EnumChoiceField`. This way, they can be declared directly in the `list_filter` iterable:
+
+```python
+from django.contrib import admin
+
+from .models import MyModel
+
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ list_filter = ('enumerated_field', )
+```
+
+
+## Usage with forms
+
+There are 2 rules of thumb:
+
+1. If you use a `ModelForm`, everything will be taken care of automatically.
+2. If you use a `Form`, you need to take into account what `Enum` and `choice_builder` you are using.
+
+
+### Usage with `django.forms.ModelForm`
+
+```python
+from .models import MyModel
+
+class ModelEnumForm(forms.ModelForm):
+ class Meta:
+ model = MyModel
+ fields = ['enumerated_field']
+
+form = ModelEnumForm({
+ 'enumerated_field': 'a'
+})
+
+form.is_valid()
+
+print(form.save(commit=True)) # <MyModel: MyModel object (12)>
+```
+
+### Usage with `django.forms.Form`
+
+If you are using the default `value_value` choice builder, you can just do that:
+
+```python
+from django_enum_choices.forms import EnumChoiceField
+
+from .enumerations import MyEnum
+
+class StandardEnumForm(forms.Form):
+ enumerated_field = EnumChoiceField(MyEnum)
+
+form = StandardEnumForm({
+ 'enumerated_field': 'a'
+})
+form.is_valid()
+
+print(form.cleaned_data) # {'enumerated_field': <MyEnum.A: 'a'>}
+```
+
+If you are passing a different choice builder, you have to also pass it to the form field:
+
+```python
+from .enumerations import MyEnum
+
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class CustomChoiceBuilderEnumForm(forms.Form):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+form = CustomChoiceBuilderEnumForm({
+ 'enumerated_field': 'Custom_a'
+})
+
+form.is_valid()
+
+print(form.cleaned_data) # {'enumerated_field': <MyEnum.A: 'a'>}
+```
+
+## Usage with `django-filter`
+
+As with forms, there are 2 general rules of thumb:
+
+1. If you have declared an `EnumChoiceField` in the `Meta.fields` for a given `Meta.model`, you need to inherit `EnumChoiceFilterMixin` in your filter class & everything will be taken care of automatically.
+2. If you are declaring an explicit field, without a model, you need to specify the `Enum` class & the `choice_builder`, if a custom one is used.
+
+### By using a `Meta` inner class and inheriting from `EnumChoiceFilterMixin`
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilterMixin
+
+class ImplicitFilterSet(EnumChoiceFilterSetMixin, filters.FilterSet):
+ class Meta:
+ model = MyModel
+ fields = ['enumerated_field']
+
+filters = {
+ 'enumerated_field': 'a'
+}
+filterset = ImplicitFilterSet(filters)
+
+print(filterset.qs.values_list('enumerated_field', flat=True))
+# <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+The `choice_builder` argument can be passed to `django_enum_choices.filters.EnumChoiceFilter` as well when using the field explicitly. When using `EnumChoiceFilterSetMixin`, the `choice_builder` is determined from the model field, for the fields defined inside the `Meta` inner class.
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilter
+
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class ExplicitCustomChoiceBuilderFilterSet(filters.FilterSet):
+ enumerated_field = EnumChoiceFilter(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+filters = {
+ 'enumerated_field': 'Custom_a'
+}
+filterset = ExplicitCustomChoiceBuilderFilterSet(filters, MyModel.objects.all())
+
+print(filterset.qs.values_list('enumerated_field', flat=True)) # <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+
+### By declaring the field explicitly on the `FilterSet`
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilter
+
+class ExplicitFilterSet(filters.FilterSet):
+ enumerated_field = EnumChoiceFilter(MyEnum)
+
+
+filters = {
+ 'enumerated_field': 'a'
+}
+filterset = ExplicitFilterSet(filters, MyModel.objects.all())
+
+print(filterset.qs.values_list('enumerated_field', flat=True)) # <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+## Postgres ArrayField Usage
+
+You can use `EnumChoiceField` as a child field of an Postgres `ArrayField`.
+
+```python
+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**
+
+```python
+instance = MyModelMultiple.objects.create(enumerated_field=[MyEnum.A, MyEnum.B])
+```
+
+**Changing enum values**
+
+```python
+instance.enumerated_field = [MyEnum.B]
+instance.save()
+```
+
+## Usage with Django Rest Framework
+
+As with forms & filters, there are 2 general rules of thumb:
+
+1. If you are using a `ModelSerializer` and you inherit `EnumChoiceModelSerializerMixin`, everything will be taken care of automatically.
+2. If you are using a `Serializer`, you need to take the `Enum` class & `choice_builder` into acount.
+
+### Using `serializers.ModelSerializer` with `EnumChoiceModelSerializerMixin`
+
+```python
+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.
+
+When using the `EnumChoiceModelSerializerMixin` with DRF's `serializers.ModelSerializer`, the `choice_builder` is automatically passed from the model field to the serializer field.
+
+### Using `serializers.ModelSerializer` without `EnumChoiceModelSerializerMixin`
+
+```python
+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()
+```
+
+If you are using a custom `choice_builder`, you need to pass that too.
+
+```python
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class CustomChoiceBuilderSerializer(serializers.Serializer):
+ enumerted_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+serializer = CustomChoiceBuilderSerializer({
+ 'enumerated_field': MyEnum.A
+})
+
+data = serializer.data # {'enumerated_field': 'Custom_a'}
+```
+
+### Using a subclass of `serializers.Serializer`
+
+```python
+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'>)])
+```
+
+If you are using a custom `choice_builder`, you need to pass that too.
+
+### Serializing PostgreSQL ArrayField
+
+`django-enum-choices` exposes a `MultipleEnumChoiceField` that can be used for serializing arrays of enumerations.
+
+**Using a subclass of `serializers.Serializer`**
+
+```python
+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`**
+
+```python
+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.
+
+## Implementation details
+
+* `EnumChoiceField` is a subclass of `CharField`.
+* Only subclasses of `Enum` are valid arguments for `EnumChoiceField`.
+* `max_length`, if passed, is ignored. `max_length` is automatically calculated from the longest choice.
+* `choices` are generated using a special `choice_builder` function, which accepts an enumeration and returns a tuple of 2 items.
+ * Four choice builder functions are defined inside `django_enum_choices.choice_builders`
+ * By default the `value_value` choice builder is used. It produces the choices from the values in the enumeration class, like `(enumeration.value, enumeration.value)`
+ * `choice_builder` can be overriden by passing a callable to the `choice_builder` keyword argument of `EnumChoiceField`.
+ * All values returned from the choice builder **will be cast to strings** when generating choices.
+
+For example, lets have the following case:
+
+```python
+class Value:
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return self.value
+
+
+class CustomObjectEnum(Enum):
+ A = Value(1)
+ B = Value('B')
+
+ # The default choice builder `value_value` is being used
+
+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:
+
+```python
+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_`:
+
+```python
+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
+
+**Fork the repository**
+```bash
+git clone https://github.com/your-user-name/django-enum-choices.git django-enum-choices-yourname
+cd django-enum-choices-yourname
+git remote add upstream https://github.com/HackSoftware/django-enum-choices.git
+```
+
+Install the requirements:
+```bash
+pip install -e .[dev]
+```
+
+Linting and running the tests:
+```bash
+tox
+```
+
+
+
+
+%package -n python3-django-enum-choices
+Summary: A custom Django field able to use subclasses of Python's internal `Enum` class as choices
+Provides: python-django-enum-choices
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-django-enum-choices
+
+# django-enum-choices (DEPRECATED)
+
+A custom Django choice field to use with [Python enums.](https://docs.python.org/3/library/enum.html)
+
+[![PyPI version](https://badge.fury.io/py/django-enum-choices.svg)](https://badge.fury.io/py/django-enum-choices)
+
+## ⚠️ Disclaimer ⚠️
+
+Starting with version 3.0, Django started supporting [Enumerations for model field choices](https://docs.djangoproject.com/en/dev/releases/3.0/#enumerations-for-model-field-choices) and we recommend using this as a native Django feature, instead of `django-enum-choices`
+
+**If you are using `django-enum-choices` and you want to upgrade your project to Django >= `3.0` you can refer to the guide in the wiki: [Migrating to Django 3](https://github.com/HackSoftware/django-enum-choices/wiki/Migrating-to-Django-3)**
+
+## Table of Contents
+
+- [django-enum-choices](#django-enum-choices)
+ - [Table of Contents](#table-of-contents)
+ - [Installation](#installation)
+ - [Basic Usage](#basic-usage)
+ - [Choice builders](#choice-builders)
+ - [Changing/Removing options from enumerations](#changingremoving-options-from-enumerations)
+ - [Changing options](#changing-options)
+ - [Removing options](#removing-options)
+ - [Usage inside the admin panel](#usage-in-the-admin-panel)
+ - [Usage with forms](#usage-with-forms)
+ - [Usage with `django.forms.ModelForm`](#usage-with-djangoformsmodelform)
+ - [Usage with `django.forms.Form`](#usage-with-djangoformsform)
+ - [Usage with `django-filter`](#usage-with-django-filter)
+ - [By using a `Meta` inner class and inheriting from `EnumChoiceFilterMixin`](#by-using-a-meta-inner-class-and-inheriting-from-enumchoicefiltermixin)
+ - [By declaring the field explicitly on the `FilterSet`](#by-declaring-the-field-explicitly-on-the-filterset)
+ - [Postgres ArrayField Usage](#postgres-arrayfield-usage)
+ - [Usage with Django Rest Framework](#usage-with-django-rest-framework)
+ - [Using `serializers.ModelSerializer` with `EnumChoiceModelSerializerMixin`](#using-serializersmodelserializer-with-enumchoicemodelserializermixin)
+ - [Using `serializers.ModelSerializer` without `EnumChoiceModelSerializerMixin`](#using-serializersmodelserializer-without-enumchoicemodelserializermixin)
+ - [Using a subclass of `serializers.Serializer`](#using-a-subclass-of-serializersserializer)
+ - [Serializing PostgreSQL ArrayField](#serializing-postgresql-arrayfield)
+ - [Implementation details](#implementation-details)
+ - [Using Python's `enum.auto`](#using-pythons-enumauto)
+ - [Development](#development)
+
+## Installation
+
+```bash
+pip install django-enum-choices
+```
+
+## Basic Usage
+
+```python
+from enum import Enum
+
+from django.db import models
+
+from django_enum_choices.fields import EnumChoiceField
+
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+**Model creation**
+
+```python
+instance = MyModel.objects.create(enumerated_field=MyEnum.A)
+```
+
+**Changing enum values**
+
+```python
+instance.enumerated_field = MyEnum.B
+instance.save()
+```
+
+**Filtering**
+
+```python
+MyModel.objects.filter(enumerated_field=MyEnum.A)
+```
+
+## Choice builders
+
+`EnumChoiceField` extends `CharField` and generates choices internally. Each choice is generated using something, called a `choice_builder`.
+
+A choice builder function looks like that:
+
+```python
+def choice_builder(enum: Enum) -> Tuple[str, str]:
+ # Some implementation
+```
+
+If a `choice_builder` argument is passed to a model's `EnumChoiceField`, `django_enum_choices` will use it to generate the choices.
+The `choice_builder` must be a callable that accepts an enumeration choice and returns a tuple,
+containing the value to be saved and the readable value.
+
+By default `django_enum_choices` uses one of the four choice builders defined in `django_enum_choices.choice_builders`, named `value_value`.
+
+It returns a tuple containing the enumeration's value twice:
+
+```python
+from django_enum_choices.choice_builders import value_value
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+print(value_value(MyEnum.A)) # ('a', 'a')
+```
+
+You can use one of the four default ones that fits your needs:
+
+* `value_value`
+* `attribute_value`
+* `value_attribute`
+* `attribute_attribute`
+
+For example:
+
+```python
+from django_enum_choices.choice_builders import attribute_value
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+class CustomReadableValueEnumModel(models.Model):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=attribute_value
+ )
+```
+
+The resulting choices for `enumerated_field` will be `(('A', 'a'), ('B', 'b'))`
+
+You can also define your own choice builder:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+def choice_builder(choice: Enum) -> Tuple[str, str]:
+ return choice.value, choice.value.upper() + choice.value
+
+class CustomReadableValueEnumModel(models.Model):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=choice_builder
+ )
+```
+
+Which will result in the following choices `(('a', 'Aa'), ('b', 'Bb'))`
+
+The values in the returned from `choice_builder` tuple will be cast to strings before being used.
+
+## Changing/Removing options from enumerations
+At any given point of time all instances of a model that has `EnumChoiceField` must have a value that is currently present in the enumeration.
+When changing or removing an option from the enumeration, a custom database migration must be made prior to the enumeration change.
+
+### Changing options
+When chaging options we'll need several operations:
+
+1. Inserting a new option with the new value that we want
+2. Migrating all instances from the old option to the new one
+3. Removing the old option and renaming the old one
+4. Removing the custom data migration code, so migrations can be run on a clean database without an `AttributeError` ocurring
+
+Example:
+
+Initial setup:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+# Desired change:
+# A = 'a_updated'
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+1. Insert a new option with the desired new value:
+```python
+class MyEnum:
+ A_UPDATED = 'a_updated'
+ A = 'a'
+ B = 'b'
+```
+```bash
+python manage.py makemigrations
+```
+
+2. Migrate model instances
+```bash
+python manage.py makemigrations app_label --empty
+```
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ MyModel = apps.get_model('app_label', 'MyModel')
+
+ MyModel.objects.filter(enumerated_field=MyEnum.A).update(enumerated_field=MyEnum.A_UPDATED)
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+```bash
+python manage.py migrate
+```
+
+3. Remove old option and rename new one
+```python
+class MyEnum:
+ A = 'a_updated'
+ B = 'b'
+```
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+4. Remove custom data migration code
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ pass
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+
+### Removing options
+Removing options from the enumeration includes several operations as well:
+
+1. Optional: Making the field nullable (if we want our existing instances' values to be `None`)
+2. Migrating all instances to a new option (or None)
+3. Removing the option from the enumeration
+4. Removing the custom data migration code, so migrations can be run on a clean database without an `AttributeError` ocurring
+
+Example:
+
+Initial setup:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+# Desired change:
+# class MyEnum(Enum):
+# A = 'a'
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+1. Optional: Make the field nullable (if you want your existing instances to have a `None` value)
+```python
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum, blank=True, null=True)
+```
+```bash
+python manage.py makemigrations
+```
+
+2. Migrate model instances
+```bash
+python manage.py makemigrations app_label --empty
+```
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ MyModel = apps.get_model('app_label', 'MyModel')
+
+ MyModel.objects.filter(enumerated_field=MyEnum.B).update(enumerated_field=MyEnum.A)
+ # OR MyModel.objects.filter(enumerated_field=MyEnum.B).update(enumerated_field=None)
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+```bash
+python manage.py migrate
+```
+
+3. Remove old option
+```python
+class MyEnum:
+ A = 'a
+```
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+4. Remove custom data migration code
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ pass
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+
+
+## Usage in the admin panel
+
+Model fields, defined as `EnumChoiceField` can be used with almost all of the admin panel's
+standard functionallities.
+
+One exception from this their usage in `list_filter`.
+
+If you need an `EnumChoiceField` inside a `ModelAdmin`'s `list_filter`, you can use the following
+options:
+
+* Define the entry insite the list filter as a tuple, containing the field's name and `django_enum_choices.admin.EnumChoiceListFilter`
+
+```python
+from django.contrib import admin
+
+from django_enum_choices.admin import EnumChoiceListFilter
+
+from .models import MyModel
+
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ list_filter = [('enumerated_field', EnumChoiceListFilter)]
+```
+
+* Set `DJANGO_ENUM_CHOICES_REGISTER_LIST_FILTER` inside your settings to `True`, which will automatically set the `EnumChoiceListFilter` class to all
+`list_filter` fields that are instances of `EnumChoiceField`. This way, they can be declared directly in the `list_filter` iterable:
+
+```python
+from django.contrib import admin
+
+from .models import MyModel
+
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ list_filter = ('enumerated_field', )
+```
+
+
+## Usage with forms
+
+There are 2 rules of thumb:
+
+1. If you use a `ModelForm`, everything will be taken care of automatically.
+2. If you use a `Form`, you need to take into account what `Enum` and `choice_builder` you are using.
+
+
+### Usage with `django.forms.ModelForm`
+
+```python
+from .models import MyModel
+
+class ModelEnumForm(forms.ModelForm):
+ class Meta:
+ model = MyModel
+ fields = ['enumerated_field']
+
+form = ModelEnumForm({
+ 'enumerated_field': 'a'
+})
+
+form.is_valid()
+
+print(form.save(commit=True)) # <MyModel: MyModel object (12)>
+```
+
+### Usage with `django.forms.Form`
+
+If you are using the default `value_value` choice builder, you can just do that:
+
+```python
+from django_enum_choices.forms import EnumChoiceField
+
+from .enumerations import MyEnum
+
+class StandardEnumForm(forms.Form):
+ enumerated_field = EnumChoiceField(MyEnum)
+
+form = StandardEnumForm({
+ 'enumerated_field': 'a'
+})
+form.is_valid()
+
+print(form.cleaned_data) # {'enumerated_field': <MyEnum.A: 'a'>}
+```
+
+If you are passing a different choice builder, you have to also pass it to the form field:
+
+```python
+from .enumerations import MyEnum
+
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class CustomChoiceBuilderEnumForm(forms.Form):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+form = CustomChoiceBuilderEnumForm({
+ 'enumerated_field': 'Custom_a'
+})
+
+form.is_valid()
+
+print(form.cleaned_data) # {'enumerated_field': <MyEnum.A: 'a'>}
+```
+
+## Usage with `django-filter`
+
+As with forms, there are 2 general rules of thumb:
+
+1. If you have declared an `EnumChoiceField` in the `Meta.fields` for a given `Meta.model`, you need to inherit `EnumChoiceFilterMixin` in your filter class & everything will be taken care of automatically.
+2. If you are declaring an explicit field, without a model, you need to specify the `Enum` class & the `choice_builder`, if a custom one is used.
+
+### By using a `Meta` inner class and inheriting from `EnumChoiceFilterMixin`
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilterMixin
+
+class ImplicitFilterSet(EnumChoiceFilterSetMixin, filters.FilterSet):
+ class Meta:
+ model = MyModel
+ fields = ['enumerated_field']
+
+filters = {
+ 'enumerated_field': 'a'
+}
+filterset = ImplicitFilterSet(filters)
+
+print(filterset.qs.values_list('enumerated_field', flat=True))
+# <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+The `choice_builder` argument can be passed to `django_enum_choices.filters.EnumChoiceFilter` as well when using the field explicitly. When using `EnumChoiceFilterSetMixin`, the `choice_builder` is determined from the model field, for the fields defined inside the `Meta` inner class.
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilter
+
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class ExplicitCustomChoiceBuilderFilterSet(filters.FilterSet):
+ enumerated_field = EnumChoiceFilter(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+filters = {
+ 'enumerated_field': 'Custom_a'
+}
+filterset = ExplicitCustomChoiceBuilderFilterSet(filters, MyModel.objects.all())
+
+print(filterset.qs.values_list('enumerated_field', flat=True)) # <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+
+### By declaring the field explicitly on the `FilterSet`
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilter
+
+class ExplicitFilterSet(filters.FilterSet):
+ enumerated_field = EnumChoiceFilter(MyEnum)
+
+
+filters = {
+ 'enumerated_field': 'a'
+}
+filterset = ExplicitFilterSet(filters, MyModel.objects.all())
+
+print(filterset.qs.values_list('enumerated_field', flat=True)) # <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+## Postgres ArrayField Usage
+
+You can use `EnumChoiceField` as a child field of an Postgres `ArrayField`.
+
+```python
+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**
+
+```python
+instance = MyModelMultiple.objects.create(enumerated_field=[MyEnum.A, MyEnum.B])
+```
+
+**Changing enum values**
+
+```python
+instance.enumerated_field = [MyEnum.B]
+instance.save()
+```
+
+## Usage with Django Rest Framework
+
+As with forms & filters, there are 2 general rules of thumb:
+
+1. If you are using a `ModelSerializer` and you inherit `EnumChoiceModelSerializerMixin`, everything will be taken care of automatically.
+2. If you are using a `Serializer`, you need to take the `Enum` class & `choice_builder` into acount.
+
+### Using `serializers.ModelSerializer` with `EnumChoiceModelSerializerMixin`
+
+```python
+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.
+
+When using the `EnumChoiceModelSerializerMixin` with DRF's `serializers.ModelSerializer`, the `choice_builder` is automatically passed from the model field to the serializer field.
+
+### Using `serializers.ModelSerializer` without `EnumChoiceModelSerializerMixin`
+
+```python
+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()
+```
+
+If you are using a custom `choice_builder`, you need to pass that too.
+
+```python
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class CustomChoiceBuilderSerializer(serializers.Serializer):
+ enumerted_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+serializer = CustomChoiceBuilderSerializer({
+ 'enumerated_field': MyEnum.A
+})
+
+data = serializer.data # {'enumerated_field': 'Custom_a'}
+```
+
+### Using a subclass of `serializers.Serializer`
+
+```python
+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'>)])
+```
+
+If you are using a custom `choice_builder`, you need to pass that too.
+
+### Serializing PostgreSQL ArrayField
+
+`django-enum-choices` exposes a `MultipleEnumChoiceField` that can be used for serializing arrays of enumerations.
+
+**Using a subclass of `serializers.Serializer`**
+
+```python
+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`**
+
+```python
+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.
+
+## Implementation details
+
+* `EnumChoiceField` is a subclass of `CharField`.
+* Only subclasses of `Enum` are valid arguments for `EnumChoiceField`.
+* `max_length`, if passed, is ignored. `max_length` is automatically calculated from the longest choice.
+* `choices` are generated using a special `choice_builder` function, which accepts an enumeration and returns a tuple of 2 items.
+ * Four choice builder functions are defined inside `django_enum_choices.choice_builders`
+ * By default the `value_value` choice builder is used. It produces the choices from the values in the enumeration class, like `(enumeration.value, enumeration.value)`
+ * `choice_builder` can be overriden by passing a callable to the `choice_builder` keyword argument of `EnumChoiceField`.
+ * All values returned from the choice builder **will be cast to strings** when generating choices.
+
+For example, lets have the following case:
+
+```python
+class Value:
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return self.value
+
+
+class CustomObjectEnum(Enum):
+ A = Value(1)
+ B = Value('B')
+
+ # The default choice builder `value_value` is being used
+
+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:
+
+```python
+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_`:
+
+```python
+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
+
+**Fork the repository**
+```bash
+git clone https://github.com/your-user-name/django-enum-choices.git django-enum-choices-yourname
+cd django-enum-choices-yourname
+git remote add upstream https://github.com/HackSoftware/django-enum-choices.git
+```
+
+Install the requirements:
+```bash
+pip install -e .[dev]
+```
+
+Linting and running the tests:
+```bash
+tox
+```
+
+
+
+
+%package help
+Summary: Development documents and examples for django-enum-choices
+Provides: python3-django-enum-choices-doc
+%description help
+
+# django-enum-choices (DEPRECATED)
+
+A custom Django choice field to use with [Python enums.](https://docs.python.org/3/library/enum.html)
+
+[![PyPI version](https://badge.fury.io/py/django-enum-choices.svg)](https://badge.fury.io/py/django-enum-choices)
+
+## ⚠️ Disclaimer ⚠️
+
+Starting with version 3.0, Django started supporting [Enumerations for model field choices](https://docs.djangoproject.com/en/dev/releases/3.0/#enumerations-for-model-field-choices) and we recommend using this as a native Django feature, instead of `django-enum-choices`
+
+**If you are using `django-enum-choices` and you want to upgrade your project to Django >= `3.0` you can refer to the guide in the wiki: [Migrating to Django 3](https://github.com/HackSoftware/django-enum-choices/wiki/Migrating-to-Django-3)**
+
+## Table of Contents
+
+- [django-enum-choices](#django-enum-choices)
+ - [Table of Contents](#table-of-contents)
+ - [Installation](#installation)
+ - [Basic Usage](#basic-usage)
+ - [Choice builders](#choice-builders)
+ - [Changing/Removing options from enumerations](#changingremoving-options-from-enumerations)
+ - [Changing options](#changing-options)
+ - [Removing options](#removing-options)
+ - [Usage inside the admin panel](#usage-in-the-admin-panel)
+ - [Usage with forms](#usage-with-forms)
+ - [Usage with `django.forms.ModelForm`](#usage-with-djangoformsmodelform)
+ - [Usage with `django.forms.Form`](#usage-with-djangoformsform)
+ - [Usage with `django-filter`](#usage-with-django-filter)
+ - [By using a `Meta` inner class and inheriting from `EnumChoiceFilterMixin`](#by-using-a-meta-inner-class-and-inheriting-from-enumchoicefiltermixin)
+ - [By declaring the field explicitly on the `FilterSet`](#by-declaring-the-field-explicitly-on-the-filterset)
+ - [Postgres ArrayField Usage](#postgres-arrayfield-usage)
+ - [Usage with Django Rest Framework](#usage-with-django-rest-framework)
+ - [Using `serializers.ModelSerializer` with `EnumChoiceModelSerializerMixin`](#using-serializersmodelserializer-with-enumchoicemodelserializermixin)
+ - [Using `serializers.ModelSerializer` without `EnumChoiceModelSerializerMixin`](#using-serializersmodelserializer-without-enumchoicemodelserializermixin)
+ - [Using a subclass of `serializers.Serializer`](#using-a-subclass-of-serializersserializer)
+ - [Serializing PostgreSQL ArrayField](#serializing-postgresql-arrayfield)
+ - [Implementation details](#implementation-details)
+ - [Using Python's `enum.auto`](#using-pythons-enumauto)
+ - [Development](#development)
+
+## Installation
+
+```bash
+pip install django-enum-choices
+```
+
+## Basic Usage
+
+```python
+from enum import Enum
+
+from django.db import models
+
+from django_enum_choices.fields import EnumChoiceField
+
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+**Model creation**
+
+```python
+instance = MyModel.objects.create(enumerated_field=MyEnum.A)
+```
+
+**Changing enum values**
+
+```python
+instance.enumerated_field = MyEnum.B
+instance.save()
+```
+
+**Filtering**
+
+```python
+MyModel.objects.filter(enumerated_field=MyEnum.A)
+```
+
+## Choice builders
+
+`EnumChoiceField` extends `CharField` and generates choices internally. Each choice is generated using something, called a `choice_builder`.
+
+A choice builder function looks like that:
+
+```python
+def choice_builder(enum: Enum) -> Tuple[str, str]:
+ # Some implementation
+```
+
+If a `choice_builder` argument is passed to a model's `EnumChoiceField`, `django_enum_choices` will use it to generate the choices.
+The `choice_builder` must be a callable that accepts an enumeration choice and returns a tuple,
+containing the value to be saved and the readable value.
+
+By default `django_enum_choices` uses one of the four choice builders defined in `django_enum_choices.choice_builders`, named `value_value`.
+
+It returns a tuple containing the enumeration's value twice:
+
+```python
+from django_enum_choices.choice_builders import value_value
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+print(value_value(MyEnum.A)) # ('a', 'a')
+```
+
+You can use one of the four default ones that fits your needs:
+
+* `value_value`
+* `attribute_value`
+* `value_attribute`
+* `attribute_attribute`
+
+For example:
+
+```python
+from django_enum_choices.choice_builders import attribute_value
+
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+class CustomReadableValueEnumModel(models.Model):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=attribute_value
+ )
+```
+
+The resulting choices for `enumerated_field` will be `(('A', 'a'), ('B', 'b'))`
+
+You can also define your own choice builder:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+def choice_builder(choice: Enum) -> Tuple[str, str]:
+ return choice.value, choice.value.upper() + choice.value
+
+class CustomReadableValueEnumModel(models.Model):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=choice_builder
+ )
+```
+
+Which will result in the following choices `(('a', 'Aa'), ('b', 'Bb'))`
+
+The values in the returned from `choice_builder` tuple will be cast to strings before being used.
+
+## Changing/Removing options from enumerations
+At any given point of time all instances of a model that has `EnumChoiceField` must have a value that is currently present in the enumeration.
+When changing or removing an option from the enumeration, a custom database migration must be made prior to the enumeration change.
+
+### Changing options
+When chaging options we'll need several operations:
+
+1. Inserting a new option with the new value that we want
+2. Migrating all instances from the old option to the new one
+3. Removing the old option and renaming the old one
+4. Removing the custom data migration code, so migrations can be run on a clean database without an `AttributeError` ocurring
+
+Example:
+
+Initial setup:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+# Desired change:
+# A = 'a_updated'
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+1. Insert a new option with the desired new value:
+```python
+class MyEnum:
+ A_UPDATED = 'a_updated'
+ A = 'a'
+ B = 'b'
+```
+```bash
+python manage.py makemigrations
+```
+
+2. Migrate model instances
+```bash
+python manage.py makemigrations app_label --empty
+```
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ MyModel = apps.get_model('app_label', 'MyModel')
+
+ MyModel.objects.filter(enumerated_field=MyEnum.A).update(enumerated_field=MyEnum.A_UPDATED)
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+```bash
+python manage.py migrate
+```
+
+3. Remove old option and rename new one
+```python
+class MyEnum:
+ A = 'a_updated'
+ B = 'b'
+```
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+4. Remove custom data migration code
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ pass
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+
+### Removing options
+Removing options from the enumeration includes several operations as well:
+
+1. Optional: Making the field nullable (if we want our existing instances' values to be `None`)
+2. Migrating all instances to a new option (or None)
+3. Removing the option from the enumeration
+4. Removing the custom data migration code, so migrations can be run on a clean database without an `AttributeError` ocurring
+
+Example:
+
+Initial setup:
+
+```python
+class MyEnum(Enum):
+ A = 'a'
+ B = 'b'
+
+# Desired change:
+# class MyEnum(Enum):
+# A = 'a'
+
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum)
+```
+
+1. Optional: Make the field nullable (if you want your existing instances to have a `None` value)
+```python
+class MyModel(models.Model):
+ enumerated_field = EnumChoiceField(MyEnum, blank=True, null=True)
+```
+```bash
+python manage.py makemigrations
+```
+
+2. Migrate model instances
+```bash
+python manage.py makemigrations app_label --empty
+```
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ MyModel = apps.get_model('app_label', 'MyModel')
+
+ MyModel.objects.filter(enumerated_field=MyEnum.B).update(enumerated_field=MyEnum.A)
+ # OR MyModel.objects.filter(enumerated_field=MyEnum.B).update(enumerated_field=None)
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+```bash
+python manage.py migrate
+```
+
+3. Remove old option
+```python
+class MyEnum:
+ A = 'a
+```
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+4. Remove custom data migration code
+```python
+# migration_name.py
+
+def forwards(apps, schema_editor):
+ pass
+
+class Migration(migrations.Migration):
+ ...
+
+ operations = [
+ migrations.RunPython(forwards),
+ ]
+```
+
+
+## Usage in the admin panel
+
+Model fields, defined as `EnumChoiceField` can be used with almost all of the admin panel's
+standard functionallities.
+
+One exception from this their usage in `list_filter`.
+
+If you need an `EnumChoiceField` inside a `ModelAdmin`'s `list_filter`, you can use the following
+options:
+
+* Define the entry insite the list filter as a tuple, containing the field's name and `django_enum_choices.admin.EnumChoiceListFilter`
+
+```python
+from django.contrib import admin
+
+from django_enum_choices.admin import EnumChoiceListFilter
+
+from .models import MyModel
+
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ list_filter = [('enumerated_field', EnumChoiceListFilter)]
+```
+
+* Set `DJANGO_ENUM_CHOICES_REGISTER_LIST_FILTER` inside your settings to `True`, which will automatically set the `EnumChoiceListFilter` class to all
+`list_filter` fields that are instances of `EnumChoiceField`. This way, they can be declared directly in the `list_filter` iterable:
+
+```python
+from django.contrib import admin
+
+from .models import MyModel
+
+@admin.register(MyModel)
+class MyModelAdmin(admin.ModelAdmin):
+ list_filter = ('enumerated_field', )
+```
+
+
+## Usage with forms
+
+There are 2 rules of thumb:
+
+1. If you use a `ModelForm`, everything will be taken care of automatically.
+2. If you use a `Form`, you need to take into account what `Enum` and `choice_builder` you are using.
+
+
+### Usage with `django.forms.ModelForm`
+
+```python
+from .models import MyModel
+
+class ModelEnumForm(forms.ModelForm):
+ class Meta:
+ model = MyModel
+ fields = ['enumerated_field']
+
+form = ModelEnumForm({
+ 'enumerated_field': 'a'
+})
+
+form.is_valid()
+
+print(form.save(commit=True)) # <MyModel: MyModel object (12)>
+```
+
+### Usage with `django.forms.Form`
+
+If you are using the default `value_value` choice builder, you can just do that:
+
+```python
+from django_enum_choices.forms import EnumChoiceField
+
+from .enumerations import MyEnum
+
+class StandardEnumForm(forms.Form):
+ enumerated_field = EnumChoiceField(MyEnum)
+
+form = StandardEnumForm({
+ 'enumerated_field': 'a'
+})
+form.is_valid()
+
+print(form.cleaned_data) # {'enumerated_field': <MyEnum.A: 'a'>}
+```
+
+If you are passing a different choice builder, you have to also pass it to the form field:
+
+```python
+from .enumerations import MyEnum
+
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class CustomChoiceBuilderEnumForm(forms.Form):
+ enumerated_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+form = CustomChoiceBuilderEnumForm({
+ 'enumerated_field': 'Custom_a'
+})
+
+form.is_valid()
+
+print(form.cleaned_data) # {'enumerated_field': <MyEnum.A: 'a'>}
+```
+
+## Usage with `django-filter`
+
+As with forms, there are 2 general rules of thumb:
+
+1. If you have declared an `EnumChoiceField` in the `Meta.fields` for a given `Meta.model`, you need to inherit `EnumChoiceFilterMixin` in your filter class & everything will be taken care of automatically.
+2. If you are declaring an explicit field, without a model, you need to specify the `Enum` class & the `choice_builder`, if a custom one is used.
+
+### By using a `Meta` inner class and inheriting from `EnumChoiceFilterMixin`
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilterMixin
+
+class ImplicitFilterSet(EnumChoiceFilterSetMixin, filters.FilterSet):
+ class Meta:
+ model = MyModel
+ fields = ['enumerated_field']
+
+filters = {
+ 'enumerated_field': 'a'
+}
+filterset = ImplicitFilterSet(filters)
+
+print(filterset.qs.values_list('enumerated_field', flat=True))
+# <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+The `choice_builder` argument can be passed to `django_enum_choices.filters.EnumChoiceFilter` as well when using the field explicitly. When using `EnumChoiceFilterSetMixin`, the `choice_builder` is determined from the model field, for the fields defined inside the `Meta` inner class.
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilter
+
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class ExplicitCustomChoiceBuilderFilterSet(filters.FilterSet):
+ enumerated_field = EnumChoiceFilter(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+filters = {
+ 'enumerated_field': 'Custom_a'
+}
+filterset = ExplicitCustomChoiceBuilderFilterSet(filters, MyModel.objects.all())
+
+print(filterset.qs.values_list('enumerated_field', flat=True)) # <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+
+### By declaring the field explicitly on the `FilterSet`
+
+```python
+import django_filters as filters
+
+from django_enum_choices.filters import EnumChoiceFilter
+
+class ExplicitFilterSet(filters.FilterSet):
+ enumerated_field = EnumChoiceFilter(MyEnum)
+
+
+filters = {
+ 'enumerated_field': 'a'
+}
+filterset = ExplicitFilterSet(filters, MyModel.objects.all())
+
+print(filterset.qs.values_list('enumerated_field', flat=True)) # <QuerySet [<MyEnum.A: 'a'>, <MyEnum.A: 'a'>, <MyEnum.A: 'a'>]>
+```
+
+## Postgres ArrayField Usage
+
+You can use `EnumChoiceField` as a child field of an Postgres `ArrayField`.
+
+```python
+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**
+
+```python
+instance = MyModelMultiple.objects.create(enumerated_field=[MyEnum.A, MyEnum.B])
+```
+
+**Changing enum values**
+
+```python
+instance.enumerated_field = [MyEnum.B]
+instance.save()
+```
+
+## Usage with Django Rest Framework
+
+As with forms & filters, there are 2 general rules of thumb:
+
+1. If you are using a `ModelSerializer` and you inherit `EnumChoiceModelSerializerMixin`, everything will be taken care of automatically.
+2. If you are using a `Serializer`, you need to take the `Enum` class & `choice_builder` into acount.
+
+### Using `serializers.ModelSerializer` with `EnumChoiceModelSerializerMixin`
+
+```python
+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.
+
+When using the `EnumChoiceModelSerializerMixin` with DRF's `serializers.ModelSerializer`, the `choice_builder` is automatically passed from the model field to the serializer field.
+
+### Using `serializers.ModelSerializer` without `EnumChoiceModelSerializerMixin`
+
+```python
+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()
+```
+
+If you are using a custom `choice_builder`, you need to pass that too.
+
+```python
+def custom_choice_builder(choice):
+ return 'Custom_' + choice.value, choice.value
+
+class CustomChoiceBuilderSerializer(serializers.Serializer):
+ enumerted_field = EnumChoiceField(
+ MyEnum,
+ choice_builder=custom_choice_builder
+ )
+
+serializer = CustomChoiceBuilderSerializer({
+ 'enumerated_field': MyEnum.A
+})
+
+data = serializer.data # {'enumerated_field': 'Custom_a'}
+```
+
+### Using a subclass of `serializers.Serializer`
+
+```python
+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'>)])
+```
+
+If you are using a custom `choice_builder`, you need to pass that too.
+
+### Serializing PostgreSQL ArrayField
+
+`django-enum-choices` exposes a `MultipleEnumChoiceField` that can be used for serializing arrays of enumerations.
+
+**Using a subclass of `serializers.Serializer`**
+
+```python
+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`**
+
+```python
+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.
+
+## Implementation details
+
+* `EnumChoiceField` is a subclass of `CharField`.
+* Only subclasses of `Enum` are valid arguments for `EnumChoiceField`.
+* `max_length`, if passed, is ignored. `max_length` is automatically calculated from the longest choice.
+* `choices` are generated using a special `choice_builder` function, which accepts an enumeration and returns a tuple of 2 items.
+ * Four choice builder functions are defined inside `django_enum_choices.choice_builders`
+ * By default the `value_value` choice builder is used. It produces the choices from the values in the enumeration class, like `(enumeration.value, enumeration.value)`
+ * `choice_builder` can be overriden by passing a callable to the `choice_builder` keyword argument of `EnumChoiceField`.
+ * All values returned from the choice builder **will be cast to strings** when generating choices.
+
+For example, lets have the following case:
+
+```python
+class Value:
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return self.value
+
+
+class CustomObjectEnum(Enum):
+ A = Value(1)
+ B = Value('B')
+
+ # The default choice builder `value_value` is being used
+
+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:
+
+```python
+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_`:
+
+```python
+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
+
+**Fork the repository**
+```bash
+git clone https://github.com/your-user-name/django-enum-choices.git django-enum-choices-yourname
+cd django-enum-choices-yourname
+git remote add upstream https://github.com/HackSoftware/django-enum-choices.git
+```
+
+Install the requirements:
+```bash
+pip install -e .[dev]
+```
+
+Linting and running the tests:
+```bash
+tox
+```
+
+
+
+
+%prep
+%autosetup -n django-enum-choices-2.1.4
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-django-enum-choices -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 2.1.4-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..399b97a
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+241acb0b42fb565c5c6a8e83922ebb3b django_enum_choices-2.1.4.tar.gz