Opened 2 years ago

Closed 2 years ago

Last modified 2 months ago

#27910 closed New feature (wontfix)

Allow using an Enum class in model Field choices

Reported by: Marcel Hellwig Owned by: nobody
Component: Database layer (models, ORM) Version: 1.10
Severity: Normal Keywords: enum model choices
Cc: Tom Forbes, Ryan Hiebert Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I will simply stick to your example here: https://docs.djangoproject.com/en/dev/ref/models/fields/#choices

I want to limit the input to certain choices so I create a list/tuple of tuples/lists. Since Python 3.4 there is a class called Enum and some nice decorators like @unique.

Currently, if you want to use this you have to do 1/2 dirty hacks, descriped in this article, which I would prefer over the current solution.

It would be nice to have native support for the Enum class, e.g. you can directly pass the class to choices (instead of using the class method choices() ), also when refering to an element in the enum just using Student.Freshmann instead of Student.Freshmann.value

A simple example would be this:

from enum import Enum

class Student(models.Model):
    class YearInSchoolChoices(Enum):
        Freshman = 'FR'
        Sophomore = 'SO'
        Junior = 'JR'
        Senior = 'SR'
    
    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchoolChoices,
        default=YearInSchoolChoices.Freshman,
    )

    def is_upperclass(self):
        return self.year_in_school in (self.YearInSchoolChoices.Junior, self.YearInSchoolChoices.Senior)

Also this could be adopted (if accepted) to any type of choices in django, e.g. Choicefield etc.

Change History (9)

comment:1 Changed 2 years ago by Simon Charette

Do you have a suggestion about how translated choices labels should be defined?

Last edited 2 years ago by Simon Charette (previous) (diff)

comment:2 Changed 2 years ago by Marcel Hellwig

To be honest, I haven't though about this, because I don't translate them. Hmm.. Good question. Implicit translation?! :/ Decorator? That are not so good ideas.
Or you could swap the positions, e.g. FR = _("Freshman") , because you'll never translate the key which is stored in the DB, right?!

comment:3 Changed 2 years ago by Simon Charette

Or you could swap the positions.

That would work for text based fields (CharField, TextField) but what about choices defined for a integer based field?

I feel like choices are a different concept than enums and that while the later is useful for referring to choices values as constants, choices and label mapping should be defined independently.

class StudentType(enum.Enum):
    Freshman = 'FR'
    Sophomore = 'SO'
    Junior = 'JR'
    Senior = 'SR'

class Student(models.Model):
    type = models.CharField(choices=[
        (StudentType.Freshman, _('Freshman')),
        (StudentType.Sophomore, _('Sophomore')),
        (StudentType.Junior, _('Junior')),
        (StudentType.Senior, _('Senior')),
    ])

comment:4 Changed 2 years ago by Marcel Hellwig

Hmm.. your solution won't bring any advantages over the current one, so.. I don't like that concept. I think the first one was okay, but we will have Problems when you want to use translation or any non-variable-names. So.. should be discard this idea (although I really do like it!)

comment:5 Changed 2 years ago by Tim Graham

Resolution: wontfix
Status: newclosed
Summary: Use Enum class in model choicesAllow using an Enum class in model Field choices

I agree with Simon's analysis. An automatic translation based on the enum value doesn't seem like a proper separate of concerns.

comment:6 Changed 20 months ago by Tom Forbes

I know this is a closed ticket, but a pattern I've has success with is a custom enum subclass that combines the values with the display:

class StudentType(EnumChoices):
    Freshman = 'FR', _('Freshman')
    Sophomore = 'SO', _('Sophomore')
    Junior = 'JR', _('Junior')
    Senior = 'SR', _('Senior')

It's somewhat simple to do, the EnumChoices class splits the initial enum values into value, display, and exposes the display text as an attribute: StudentType.Freshman.display (and in the str method). list(StudentType) returns a list of tuples that is suitable for choices.

Could this not be an OK compromise? In the applications I've seen with large numbers of choices managing the display values gets to be a burden, the values should indeed be separate from the display and not auto-generated but there is a tangible benefit in keeping their definitions as close as possible together. Splitting the definition of an enum out into two parts in the django-way seems to violate DRY, no?

comment:7 Changed 20 months ago by Tom Forbes

Cc: Tom Forbes added

comment:8 Changed 20 months ago by Marcel Hellwig

I like the solution Tim brought up. Maybe making the second item on the enum optional, so that you could still write

    Freshman = 'FR',

and for default just use an untranslated name of the field (e.g. Freshman).

How do you like it?

comment:9 Changed 2 months ago by Ryan Hiebert

Cc: Ryan Hiebert added
Note: See TracTickets for help on using tickets.
Back to Top