Using Enums as Django Model Choices
Tags: django, python, djangomodel
Published: 2019-08-23 11:45:26

#Our goal on this quick overview is to get our model choices more organised and readable, yes its a little more work but the payoff is worth it.


###Note
As always the code provided is not production ready, it is an example show casing some techniques you can use to solve a particular problem or task allowing you to implement this into your own code and workflow. Always learn it first, don’t just copy and paste 😅 if you do, make sure you add test’s, error handling, etc

###Enums in Python
We can use python’s built in Enum class to create clear, reusable constants not just for models but available to be used elsewhere. If you are unsure of the concept of Enums you can review the python docs here.

###Why? When tuples work fine…
Good point…It does work fine and Iam not saying you shouldn’t use that as an option. This is another approach or tool in your tool box which can be used when you need it and its appropriate.

###Reasoning
Enums are best utilised to represent an immuntable set of values, for instance this would be days of the week and months or some arbitary set of constants in your application.

###Scenario
Our imaginary crm system Cake CRM, currently makes use of a tuple for customer type. We have run into an issue where engineers are using the wrong values in comparions, we also want to use the values elsewhere in our application.

###Example
Our current model is this:

{% gist https://gist.github.com/bencleary/0e013de28dc1ac90580e5d215dd987d2 %}

Lets create a new file utils.py in our application, and lets transfer customer_types to an Enum and update our model so we can work with Enums:

{% gist https://gist.github.com/bencleary/e84709e3acf978725f192df6b5b9c5bb %}

{% gist https://gist.github.com/bencleary/87c33623fa2f61e5e58b6e8f3b01b6aa %}

Lets breakdown what we did:

* Our customer types is now a class extending from IntEnum
You have the choice to use Enum or IntEnum, make sure you choose the right one for your field type, you can’t use ints on a normal Enum
* Our choices are now properties of that class
* We implemented a @classmethod called choices which returns an interable usable by Django’s ORM, currently you cannot just pass an Enum to a model

> We have imported our Enum into our models.py and changed the choices argument to our Class calling the choices method

* Our default has now changed from 1 to a representative value of CustomerTypes.PROSPECT
* We have also implemented a custom method on the model to return the titlised name of the Enum option

> Trying to use get_field_name_display will always return the all caps property name, you cannot override this without monkey patching or changing how models are instatiated, but the value can be altered after the fact for instance using customer.get_type_display|title in templates

You are now free to use these value representations elsewhere for example we wanted to use this out of the scope of models, you can simply do something like this:

{% gist https://gist.github.com/bencleary/0e013de28dc1ac90580e5d215dd987d2 %}

This approach although a few more steps does provide a better experience when dealing with constants in Django, some of the benefits in my opinion are:

* Flexibilty, they aren’t tied to an model class, or just random code snippet in your models.py
* Better readabilty, no more trying to understand what the random 1 or two letter code means
* Ordered and structured approach to collating constants used by models

Again there is nothing wrong with using tuples, this is another tool and approach of solving a specific problem.

Keep going & keep coding 👍