Reputation: 3341
I'm using enums in a django model, like:
class AwesomeNess(Enum):
slight = "SLIGHT"
very = "VERY"
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
awesomeness = models.CharField(
max_length=255,
choices=[(tag.value, tag.name) for tag in AwesomeNess],
default=AwesomeNess.slight
)
This works fine when I use Django's filter function, like:
d = Choice.objects.filter(awesomeness=AwesomeNess.slight)
However, it does not work if I do:
choice_obj = Choice.objects.get(id=1)
choice_obj.awesomeness == AwesomeNess.slight # will return False
choice_obj.awesomeness == "AwesomeNess.slight" # will return True
Since values are stored as strings, it looks like Django forgets to cast them back to an enum when returning the data. This gives inconsistencies when coding, since django model filters can query on the enum, while attribute equality filtering requires me to use the stringed representation of the enum.
EDIT: The enums are imported from another model class, so replacing it with Django's built-in choices class is not an option.
EDIT2: As i'm investigating this, I'm also noticing the following:
myobj = Choice.objects.get(id=1)
myobj.awesomeness # <<-- string type
myobj.awesomeness = AwesomeNess.very <<-- object changes type to enum
myobj.save()
myobj2 = Choice.objects.get(id=1) <<-- upon reload from db, the updated value is returned as string
As a novice Django user, this seems incredibly dangerous to me, and isn't mentioned in any of the "how to use enums with django" articles I've read. Is this simply due to the fact that I'm on a too old Django version?
Is there a way around this? Am I doing something very wrong? Pointers appreciated! oh, bwt: this is on Django 2.2.24. Maybe later versions have improved enum support?
Upvotes: 3
Views: 1598
Reputation: 477180
The reason that this happens is because the choices call str
on the value that is given, and this thus means it will store "AwesomeNess.slight"
as a string (if that is set as default).
Since django-3.0, you can work with a TextChoices
class [Django-doc] which is quite the same as using an Enum
. You thus can define a TextChoices
model with:
class Choice(models.Model):
class AwesomNess(models.TextChoices):
SLIGHT = 'SLIGHT', 'slight'
VERY = 'VERY', 'very'
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
awesomeness = models.CharField(
max_length=255,
choices=AwesomeNess.choices,
default=AwesomeNess.SLIGHT
)
for older versions, you need to specify that you work with the value of the AwesomeNess
:
class AwesomeNess(Enum):
slight = 'SLIGHT'
very = 'VERY'
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
awesomeness = models.CharField(
max_length=255,
choices=[(tag.value, tag.name) for tag in AwesomeNess],
default=AwesomeNess.slight.value
)
Upvotes: 5