How to use enums as a choice field in django model
Django 3.0 has built-in support for Enums
Example:
from django.utils.translation import gettext_lazy as _ class Student(models.Model): class YearInSchool(models.TextChoices): FRESHMAN = 'FR', _('Freshman') SOPHOMORE = 'SO', _('Sophomore') JUNIOR = 'JR', _('Junior') SENIOR = 'SR', _('Senior') GRADUATE = 'GR', _('Graduate') year_in_school = models.CharField( max_length=2, choices=YearInSchool.choices, default=YearInSchool.FRESHMAN, )
These work similar to enum from Python’s standard library, but with some modifications:
- Enum member values are a tuple of arguments to use when constructing the concrete data type. Django supports adding an extra string value to the end of this tuple to be used as the human-readable name, or
label
. Thelabel
can be a lazy translatable string. Thus, in most cases, the member value will be a(value, label)
two-tuple. If a tuple is not provided, or the last item is not a (lazy) string, the label is automatically generated from the member name.- A
.label
property is added on values, to return the human-readable name. A number of custom properties are added to the enumeration classes –.choices
,.labels
,.values
, and.names
– to make it easier to access lists of those separate parts of the enumeration. Use.choices
as a suitable value to pass to choices in a field definition.- The use of
enum.unique()
is enforced to ensure that values cannot be defined multiple times. This is unlikely to be expected in choices for a field.
For more info, check the documentation
Note:
As @Danielle Madeley pointed out, if you try to access the year_in_school
attribute directly Django still returns the raw string instead of the Enum object:
>>> student.year_in_school
'FR'
What I usually do is to create a helper method that returns the Enum object:
class Student(models.Model):
...
def get_year_in_school(self) -> YearInSchool:
# Get value from choices enum
return self.YearInSchool[self.year_in_school]
Problem in your code is that INITIATED = "INITIATED",
a comma after INITIATED
option and other options. when we add comma after any string it will become a tuple. See an example below
s = 'my str'
print(type(s))
# output: str
s = 'my str',
print(type(s))
# output: tuple
#models.py
class Transaction(models.Model):
trasaction_status = models.CharField(max_length=255, choices=TransactionStatus.choices())
transaction_type = models.CharField(max_length=255, choices=TransactionType.choices())
#enums.py
class TransactionType(Enum):
IN = "IN"
OUT = "OUT"
@classmethod
def choices(cls):
print(tuple((i.name, i.value) for i in cls))
return tuple((i.name, i.value) for i in cls)
class TransactionStatus(Enum):
INITIATED = "INITIATED"
PENDING = "PENDING"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
ERROR = "ERROR"
@classmethod
def choices(cls):
print(tuple((i.name, i.value) for i in cls))
return tuple((i.name, i.value) for i in cls)
For django > 3.0 https://docs.djangoproject.com/en/4.0/ref/models/fields/#field-choices-enum-types
For Django 2.x and lower:
You define an Enum
by setting the various options as documented here:
class TransactionStatus(Enum):
INITIATED = "INITIATED"
PENDING = "PENDING"
COMPLETED = "COMPLETED"
FAILED = "FAILED"
ERROR = "ERROR"
Note there are no commas! This allows you later in your code to refer to TransactionStatus.ERROR
or TransactionStatus.PENDING
.
The rest of your code is correct. You get the choices
by creating tuples of option.name
, option.value
.
UPDATE: For Django 3.x and higher, use the built-in types TextChoices
, IntegerChoices
and Choices
as described here. That way you don't have to construct the choices
tuple yourself.