Django Enum
Django Enum provides full and natural support for enumerations as Django model fields, integrating with `enum-properties`. It allows defining robust enum fields that seamlessly store and retrieve enum members in the database, offering advanced features like properties and flags. As of version 2.4.2, it supports Django 4.2+ and Python 3.10+, with regular patch and minor releases.
Common errors
-
django.core.exceptions.ImproperlyConfigured: django-enum requires Django 4.2 or higher. (or similar error for Python version incompatibility)
cause Attempting to use `django-enum` version 2.3.0 or later with an unsupported Django (<4.2) or Python (<3.10) version.fixUpgrade your Django installation to 4.2+ and your Python version to 3.10+. Alternatively, if upgrading is not feasible, downgrade `django-enum` to a compatible version (e.g., `pip install django-enum<2.3.0`). -
TypeError: 'tuple' object cannot be interpreted as an integer (or similar TypeError when accessing enum member properties like `.label`)
cause You defined an `enum_properties.Choices` member with a raw tuple, such as `STATUS = ('P', 'Pending')`, instead of using the `p()` helper. This makes the member's value the tuple itself, preventing correct property access.fixModify your enum definition to use `enum_properties.p()` for members that require distinct value and label properties. Example: `from enum_properties import p; class MyStatus(Choices): PENDING = p('P', 'Pending')`. -
AttributeError: 'str' object has no attribute 'name' (or '.label' or '.value') when accessing enum field
cause This usually happens when you're mistakenly trying to access enum properties on the raw string value stored in the database, rather than the `EnumField` instance itself, or if the field somehow isn't converting the database value back to an enum member.fixEnsure you are accessing the field correctly on the model instance (e.g., `my_model.status.label`) and that `django-enum` is properly installed and configured in `INSTALLED_APPS`.
Warnings
- breaking Versions of `django-enum` 2.3.0 and above dropped support for Django versions 3.2-4.1 and Python 3.9. Running these newer versions in older environments will cause `ImproperlyConfigured` or `ModuleNotFoundError` errors.
- gotcha When defining `enum_properties.Choices` (or `Flag`) members, tuple values (e.g., `NAME = ('value', 'Label')`) are NOT automatically unpacked into `value` and `label` properties as they might be with Django's native `Choices`. The member's value will literally be the tuple.
- gotcha EnumField values may not display correctly or filter as expected in the Django Admin's `list_display` or `list_filter` if the underlying `enum-properties` enum members lack proper hash equivalency (`__hash__` and `__eq__` methods).
Install
-
pip install django-enum
Imports
- EnumField
from django_enum import EnumField
- Choices
from django.db.models import Choices
from enum_properties import Choices
- Flag
from enum_properties import Flag
- p
from enum_properties import p
Quickstart
import os
import django
from django.conf import settings
from django.db import models
from enum_properties import Choices, p
from django_enum import EnumField
# Minimal Django settings for a runnable example
settings.configure(
DEBUG=True,
INSTALLED_APPS=["django.contrib.auth", "django.contrib.contenttypes", "myapp"],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
# Ensure 'myapp' is recognized by Django for migrations
TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, }],
ROOT_URLCONF='django_enum.urls' # Dummy, or define a minimal one if needed
)
django.setup()
# Define an enum using enum_properties.Choices and the p() helper
class Status(Choices):
PENDING = p('P', 'Pending')
APPROVED = p('A', 'Approved')
REJECTED = p('R', 'Rejected')
# Define a Django model using EnumField
class Order(models.Model):
name = models.CharField(max_length=100)
status = EnumField(Status, default=Status.PENDING)
class Meta:
app_label = 'myapp' # Required for standalone model in Django setup
def __str__(self):
return f"Order '{self.name}' - Status: {self.status.label}"
# Example usage:
if __name__ == '__main__':
# Apply migrations (simplified for quickstart without manage.py)
from django.core.management import call_command
from django.db.migrations.executor import MigrationExecutor
from django.db import connection
# Django needs to discover the models for migration, so we manually register an app
from django.apps import apps
apps.populate(settings.INSTALLED_APPS)
executor = MigrationExecutor(connection)
executor.loader.build_graph()
targets = executor.loader.graph.leaf_nodes()
executor.migrate(targets)
print('Migrations applied for myapp.')
# Create an instance
order1 = Order.objects.create(name='Laptop', status=Status.PENDING)
order2 = Order.objects.create(name='Keyboard', status=Status.APPROVED)
print(f"Created: {order1}")
print(f"Created: {order2}")
# Retrieve and interact with the enum field
retrieved_order = Order.objects.get(name='Laptop')
print(f"Retrieved order status (name): {retrieved_order.status.name}")
print(f"Retrieved order status (value): {retrieved_order.status.value}")
print(f"Retrieved order status (label): {retrieved_order.status.label}")
# Filtering by enum member
approved_orders = Order.objects.filter(status=Status.APPROVED)
print(f"Approved orders: {[str(o) for o in approved_orders]}")
# Filtering by enum value
pending_orders = Order.objects.filter(status__value='P')
print(f"Pending orders (by value): {[str(o) for o in pending_orders]}")