How to Create Django Like Choices Field in Flask SQLAlchemy

Erika Dike
The Andela Way
Published in
4 min readAug 2, 2019
Photo by Chris Abney on Unsplash

One thing you miss when you move from one framework or language to another are the tools that come for free in your other framework, particularly if you are moving from a beast like Django to something minimal like Flask.

When creating models for your applications, you routinely encounter fields that should only have a fixed set of values. A field to hold one’s employment status would probably have just two values: employed and unemployed. In such scenarios, it would make sense to enforce that only these two options can be inserted into the employment status field.

With Django, all you have to do is create a choices field. This field type is supported out of the box and is one of the first things you encounter when learning Django. The code for implementing a choices field in Django would look like this:

from django.db import models class Person(models.Model):
EMPLOYED = 'EMPLOYED'
UNEMPLOYED = 'UNEMPLOYED'
EMPLOYMENT_STATUS_CHOICES = [
(EMPLOYED, 'Employed')
(UNEMPLOYED, 'Unemployed')
]
employment_status = models.CharField(
max_length=10,
choices=EMPLOYMENT_STATUS_CHOICES,
default=EMPLOYED,
)

Once set up, you can populate a field elsewhere in your code like so

new_peson.employment_status = Person.EMPLOYED

Flask, however, doesn’t have an equivalent concept (at least as far as my investigations have taken me). Still, you can achieve similar functionality by using enums. Enumerations in Python are implemented by using the module enum. In an enum, you have a set of symbolic names that are bound to unique, constant values.

Django also supports enums but enums would probably not be the first tool you reach for when implementing a fixed choices field in Django.

The code below allows us to implement an employment_status fixed choices field in Flask.

import enumclass EmploymentStatusEnum(enum.Enum):
employed = 'employed'
unemployed = 'unemployed'

class Person(db.Model):
__tablename__ = 'persons'
employment_status = db.Column(
db.Enum(EmploymentStatusEnum),
default=EmploymentStatusEnum.employed,
nullable=False
)

You write the following the populate the field

new_person.employment_status = EmploymentStatusEnum.employed

One might argue that the enums approach is much cleaner. Well, to each their own. But I will say I have become a fan and might begin to use enums in future Django projects.

The approach above works well for strings but what happens when your fixed set of values are integers and you would like to perform arithmetic operations on them. A naive solution would be to convert string digits to integers just before performing arithmetic operations. However, there is a solution that allows us to save the digits as integers right in the database. This solution requires two pieces.

The first piece is a new class that enables us to pass in a Python enum and store the enum’s value in the database as an integer. The default enum behaviour is to store the enum’s name which would have to be a string since we can’t have numbers as the names of variables in Python. In our previous employment status example, the field name employed is stored in the database, not the value ‘employed’. It didn’t matter there because both the name and value were the same. However, say, we have a new field: number_of_jobs with options 0–5. Our enum would look like this:

import enumclass NumberJobsEnum(enum.Enum):
none = 0
one = 1
two = 2
three = 3
four = 4
five = 5

By default, the name field would be stored in the database. But we want the value field to be stored instead. That is where a new class comes in; to change that behaviour so the value is stored rather than the name of the enum field.

import enumclass IntEnum(db.TypeDecorator):
impl = db.Integer()
def __init__(self, enumtype, *args, **kwargs):
super().__init__(*args, **kwargs)
self._enumtype = enumtype
def process_bind_param(self, value, dialect):
if isinstance(value, Enum):
return value
elif isinstance(value, int):
return value
return value.value def process_result_value(self, value, dialect):
return self._enumtype(value)

The value returned by theprocess_bind_param method is what gets saved in the database. There, if the value passed in is an integer or IntEnum type, we return that value, else we return the value attribute of the enum. So if we are passed the enum two, we return its value, which is 2.

process_result_value is what is returned whenever we call the field in our code, e.g. new_person.number_of_jobs. What it does is to cast the value in the database back to an enum object.

The second piece to our solution is similar to what we have done in the past, in how we define our enum and use it in a field. Except, this time, we are going to be using the IntEnum class we created.

class NumberJobsEnum(enum.IntEnum):
none = 0
one = 1
two = 2
three = 3
four = 4
five = 5
class Person(db.Model):
__tablename__ = 'persons'
number_of_jobs = db.Column(
IntEnum(NumberJobsEnum),
default=NumberJobsEnum.none,
nullable=False
)

And that is all you need to do to get the int values into the database. With this, you can perform operations such as

total_jobs = person_1.number_of_jobs + person_2.number_of_jobs

So when next you are defining a field with a fixed set of options in Flask, consider using the enum type to specify explicitly what those values should be. This practice expels a number of bugs in our applications that result when users enter a value in a format or spelling that we do not expect. Save yourself all that error handling and enforce at the database level what values are allowed in a field.

--

--

Erika Dike
The Andela Way

I write software and occasionally publish stuff about some things I found interesting.