Search code examples
pythondjangoenumsmodelchoicefielddjango-model-field

Using integers and strings together in Django choice field


I am trying to use the new Enum types in the latest Django version for the choice field. Specifically I am trying to store the various states of United States as follows:

class States(models.TextChoices):
    ALABAMA = 'AL', 'Alabama'
    ALASKA = 'AK', 'Alaska'
    .....
    .....
    WISCONSIN = 'WI', 'Wisconsin'
    WYOMING = 'WY', 'Wyoming'


class PersonalInfo(models.Model):
    state = models.CharField(max_length=2, choices=States.choices, default=States.ALABAMA)

Works as expected. Now, I am also trying to make the max_length variable also a class attribute of the choice class to make the code more modular by doing the following:

class States(models.TextChoices):
    ALABAMA = 'AL', 'Alabama'
    ALASKA = 'AK', 'Alaska'
    .....
    .....
    WISCONSIN = 'WI', 'Wisconsin'
    WYOMING = 'WY', 'Wyoming'
    MAX_LENGTH = 2
    

class PersonalInfo(models.Model):
    state = models.CharField(max_length=States.MAX_LENGTH, choices=States.choices, default=States.ALABAMA)

This gives me an error as follows:

if self.max_length is not None and choice_max_length > self.max_length:
TypeError: '>' not supported between instances of 'int' and 'States'

I understand that Django also provides an alternative IntegerChoices for integer but how do I use both text and integer choices together.


Solution

  • TextChoices has to contain string values and the way these work is that they enumerate what you define in the class. So you've got a mixture of types which won't work because it'll try to include 2 as one of the choices, because it's part of the choices class.

    What you could do is define the choices as a constant a bit like this;

        STATE_MAX_LENGTH = 2
    
    
        class States(models.TextChoices):
            ALABAMA = 'AL', 'Alabama'
            ALASKA = 'AK', 'Alaska'
            .....
            .....
            WISCONSIN = 'WI', 'Wisconsin'
            WYOMING = 'WY', 'Wyoming'
            
    
        class PersonalInfo(models.Model):
            state = models.CharField(max_length=STATE_MAX_LENGTH, choices=States.choices, default=States.ALABAMA)
    

    To confirm, here are the choice classes from django;

    class Choices(enum.Enum, metaclass=ChoicesMeta):
        """Class for creating enumerated choices."""
    
        def __str__(self):
            """
            Use value when cast to str, so that Choices set as model instance
            attributes are rendered as expected in templates and similar contexts.
            """
            return str(self.value)
    
    
    class IntegerChoices(int, Choices):
        """Class for creating enumerated integer choices."""
        pass
    
    
    class TextChoices(str, Choices):
        """Class for creating enumerated string choices."""
    
        def _generate_next_value_(name, start, count, last_values):
            return name