#30774 closed Bug (fixed)
Migrations uses value of enum object instead of its name.
Reported by: | oasl | Owned by: | Hasan Ramezani |
---|---|---|---|
Component: | Migrations | Version: | dev |
Severity: | Normal | Keywords: | Enum, Migrations |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When using Enum object as a default value for a CharField, the generated migration file uses the value of the Enum object instead of the its name. This causes a problem when using Django translation on the value of the Enum object.
The problem is that, when the Enum object value get translated to the users language, the old migration files raise an error stating that the Enum does not have the corresponding value. (because the Enum value is translated to another language)
Example:
Let say we have this code in models.py:
from enum import Enum from django.utils.translation import gettext_lazy as _ from django.db import models class Status(Enum): GOOD = _('Good') # 'Good' will be translated BAD = _('Bad') # 'Bad' will be translated def __str__(self): return self.name class Item(models.Model): status = models.CharField(default=Status.GOOD, max_length=128)
In the generated migration file, the code will be:
... ('status', models.CharField(default=Status('Good'), max_length=128)) ...
After the translation, 'Good' will be translated to another word and it will not be part of the Status Enum class any more, so the migration file will raise the error on the previous line:
ValueError: 'Good' is not a valid Status
Shouldn't the code generated by the migration uses the name of the Status Enum 'GOOD', not the value of it, since it is changeable?
It should be:
('status', models.CharField(default=Status['GOOD'], max_length=128))
This will be correct regardless of the translated word
Change History (13)
comment:1 by , 5 years ago
Description: | modified (diff) |
---|
comment:2 by , 5 years ago
Resolution: | → needsinfo |
---|---|
Status: | new → closed |
Summary: | Migrations uses value of enum object instead of its name → Migrations uses value of enum object instead of its name. |
Version: | 2.2 → master |
comment:3 by , 5 years ago
Resolution: | needsinfo |
---|---|
Status: | closed → new |
To experience the bug:
In any Django project, set the default value of a CharField as an enum object:
class EnumClass(Enum): VALUE = _('Value')
where:
VALUE: is the constant enum object name
'Value': is the translatable enum object value
In the model:
field = models.CharField(default=EnumClass.VALUE, max_length=128)
then run: python manage.py makemigrations
In the generated migration file, you will notice that the default value of the field is set to: EnumClass('Value'), so it calls the enum object by its value not it is constant name.
run: python manage.py migrate
In the settings.py file:
LANGUAGE_CODE = 'fr-FR' # set it to any language code other than English
Run the project after generating, translating, and compiling the messages file (see: message-files)
The project will raise the error: ValueError: 'Value' is not a valid EnumClass
, on the generated migration file.
comment:4 by , 5 years ago
Triage Stage: | Unreviewed → Accepted |
---|
This use case looks quite niche for me, i.e. I would expect to store a unified values (the same for all languages) and translate only labels visible for users, however I agree that we can fix this.
comment:5 by , 5 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 5 years ago
Here is the diff based on the @oasl solution
Shouldn't the code generated by the migration uses the name of the Status Enum 'GOOD', not the value of it, since it is changeable? It should be: ('status', models.CharField(default=Status['GOOD'], max_length=128))
diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py index 27b5cbd379..b00c6f0df2 100644 --- a/django/db/migrations/serializer.py +++ b/django/db/migrations/serializer.py @@ -120,9 +120,9 @@ class EnumSerializer(BaseSerializer): def serialize(self): enum_class = self.value.__class__ module = enum_class.__module__ - v_string, v_imports = serializer_factory(self.value.value).serialize() + _, v_imports = serializer_factory(self.value.value).serialize() imports = {'import %s' % module, *v_imports} - return "%s.%s(%s)" % (module, enum_class.__name__, v_string), imports + return "%s.%s['%s']" % (module, enum_class.__name__, self.value), imports
@felixxm, what do you think?
comment:7 by , 5 years ago
You cannot use a string representation of self.value
i.e. 'EnumClass.GOOD'
, IMO we should use a name
property:
return "%s.%s[%r]" % (module, enum_class.__name__, self.value.name), imports
comment:9 by , 5 years ago
Needs tests: | set |
---|
comment:10 by , 5 years ago
Needs tests: | unset |
---|
comment:11 by , 5 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
Thanks for this report, however I'm not sure how translated values can brake migrations. Can you provide a sample project to reproduce this issue? Migrations with translatable strings works fine for me: