简体   繁体   中英

Enum using Django ModelField choices as string — anti-pattern?

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?

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])

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 .

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) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM