Django Model Choices using Python Enum

Python 3.4 introduced enums types which seem tailor made for use with django model choices. I couldn’t find any examples of how to use enums in django, so this is how you can use them. It’s relatively simple with some tricks, though you may call them warts. django does need to enable enums better. Python enums have been back ported to prior python versions via the enum34 package (“pip install enum34”), in case you want to give it a shot in python 2.

In this example, we’ll use an IntEnum to associate a variable with an integer value. For django, this means that the database contains integer values, though we have a descriptive text for each choice. This is desirable, as we can abstract the name of the variable from the actual value. If you change / refactor the name, the existing values will simply remain the same. Or in other words, it’s good software engineering practise.

Here’s the model definition:

from enum import IntEnum, unique 
def django_enum(cls): 
# decorator needed to enable enums in django templates
cls.do_not_call_in_templates = True
return cls
class MyModel(Model): 
# store the choices in the model, so that they are readily
# accessible wherever the model is.
# unique ensures all variables are unique
@unique
@django_enum
class MyChoices(IntEnum):
ready = 0
set = 10
go = 20
complete = 100
    # transform enum into choices conform format [(value, str)] 
# also replacing underscore with a space for readability
status = IntegerField(choices=[(choice.value, choice.name.replace("_", " ")) for choice in MyChoices])

You can now access this in your template as you would expect:

{% if mymodel.status == mymodel.MyChoices.ready %} 
We are ready! {{ mymodel.MyChoices.ready.name }}
{% elif mymodel.status >= mymodel.MyChoices.set %}
Set, go or complete: {{ mymodel.get_status_display }}
{% endif %}

In python, the enums can be accessed by their value or their name:

# let's display the name 
MyModel.MyChoices.ready.name
# let's display the value MyModel.MyChoices.ready.value
# python will automatically compare the int value when comparing. This returns True MyModel.MyChoices.ready == 0
# this will return 'MyChoices.ready'
str(MyModel.MyChoices.ready)

That’s all I’ve needed so far. Read up more on the official docs at
 https://docs.python.org/3/library/enum.html . Clearly, this is somewhat sub-optimal and needs to be enabled by the django framework itself. Otherwise it is a little like a duck tape solution.

If you want to know why you need the django_enum decorator, read this:
 http://stackoverflow.com/questions/35953132/how-to-access-enum-types-in-django-templates


Originally published at ricw.svbtle.com.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.