5

I have a Django field that I'm using basically as an enum for notification preferences.

Right now I have it set up like so:

class MyModel(models.Model):
    # ...
    EVERY_TIME = 'every'; WEEKLY = 'weekly'; NEVER = 'never'
    NOTIFICATION_CHOICES = ((EVERY_TIME, "Every time"), (WEEKLY, "Weekly"), (NEVER, "Never"))
    notification_preferences = models.CharField(choices=NOTIFICATION_CHOICES, default=EVERY_TIME, max_length=10)

I know that generally this kind of enum should be set up as a models.IntegerField rather than a CharField, but since the front-end uses Angular and the data is all served via an API, I feel like it might provide a bit more useful information for the front-end to receive 'weekly' rather than 2, for example.

Is it considered bad practice to use a CharField as an enum? If so, is my use case small enough that it's not a big deal, or is there something I'm missing that I should change it?

1
  • 1
    more pythonic to do tuple assignment of your frequency variables: EVERY_TIME, WEEKLY, NEVER = 'every', 'weekly', 'never' Commented Dec 15, 2014 at 22:46

5 Answers 5

4

You might want to consider the Django-model-utils Choices library, which would give you some more control over the text versions of the enum.

To answer your question, not everything lends itself to an integer identifier. Consider states, Australia has 7 and they have a fixed when known set:

ACT - Australian Capital Territory
NSW - New South Wales
NT  - Northern Territory
QLD - Queensland
SA  - South Australia
TAS - Tasmania
VIC - Victoria
WA  - Western Australia

Since these are relatively fixed (the make up of the country is unlikely to change, there is no reason to assign an integer to each, when the textual coding, with the full name works just as well.

I wouldn't say that using a CharField as a choice is an anti-pattern, just an approach that should only be applied if you are certain that the abbreviated versions for the database make sense when stored as text.

Also you can use Enum to store your values

from enum import Enum

class Countries(Enum):
   ACT = "Australian Capital Territory"
   NSW = "New South Wales"
   ...

class MyModel(models.Model):
    country = models.CharField(choices=[(tag.name, tag.value) for tag in Countries])
Sign up to request clarification or add additional context in comments.

3 Comments

Sadly I can't take credit for it, but it is awesome - and practically essential.
This unfortunately doesn't answer my question though, which was more regarding efficiency, as much as Choice is still a great tool. It looks like they have examples though with choices as Charfield, so I guess there's my answer.
@jdotjdot I've added somemore context that should answer the question.
3

Is it considered bad practice to use a CharField as an enum? If so, is my use case small enough that it's not a big deal, or is there something I'm missing that I should change it?

You are looking at this entirely the wrong way. The choice of field depends entirely on the data you want to store. So, the question isn't if CharField is bad practice for enum, the question you need to ask is:

"What do I need to store in my database for this piece of information?"

There is a school of thought that says that you should only store integers in the database if you want the database to help you do calculations on that field, otherwise, you should store numbers as a string type, because this gives you more flexibility.

I am not going to go much deeper into that because its an opinionated topic, what I am going to say is if you decide that storing the data as characters makes sense for your application, then just do it. Do not over cook this in your mind.

I know that generally this kind of enum should be set up as a models.IntegerField rather than a CharField, but since the front-end uses Angular and the data is all served via an API, I feel like it might provide a bit more useful information for the front-end to receive 'weekly' rather than 2, for example.

I am not sure where you are drawing your conclusions about this, but there is no hard and fast rule about what datatype should enums refer to. This is up to your application and what information you need to store - and to decide that you need to know how you will query your database for this information.

In your case, you are looking at this from a API usability perspective, and that is perfectly fine. Your main purpose of this model is to store information and that information should be stored in a way that provides the most utility to your application.

Finally, do not use ; in Python. Although technically not incorrect, this is generally frowned upon and does not conform to the python style guide.

1 Comment

Regarding the ;, while I am well aware of what the Python style guide says, for a small list of choice assignments like this I like to do use ;, and it's about the only time I do. When you put each choice assignment on its own line it ends up taking up an enormous amount of room in the model, and it must be declared before the field in question so I can't just stick them all at the bottom, and it makes my models bulkier than I like--so I (and others I've seen) do it this way.
1

This is supported natively in Django 3+

https://adamj.eu/tech/2020/01/27/moving-to-django-3-field-choices-enumeration-types/

Comments

0

Using a CharField for an enum is definitely NOT an anti-pattern anymore, considering that the latest Django documentation recommends this approach in its choices example, reprinted here:

FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
YEAR_IN_SCHOOL_CHOICES = (
    (FRESHMAN, 'Freshman'),
    (SOPHOMORE, 'Sophomore'),
    (JUNIOR, 'Junior'),
    (SENIOR, 'Senior'),
)
year_in_school = models.CharField(max_length=2,
                                  choices=YEAR_IN_SCHOOL_CHOICES,
                                  default=FRESHMAN) 

Comments

-1

There is a build-in solution already: https://docs.djangoproject.com/en/1.11/ref/models/instances/#django.db.models.Model.get_FOO_display

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.