The problem is that, when the Enum object value get translated to the
users language, the migrations 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
--
Ticket URL: <https://code.djangoproject.com/ticket/30774>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
Old description:
New description:
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
Example:
def __str__(self):
return self.name
--
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:1>
* status: new => closed
* version: 2.2 => master
* resolution: => needsinfo
Comment:
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:
{{{
>>> class TextEnum(enum.Enum):
... C = _('translatable value')
...
>>> TextEnum(_('translatable value'))
<TextEnum.C: 'translatable value'>
>>> TextEnum('translatable value')
<TextEnum.C: 'translatable value'>
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:2>
* status: closed => new
* resolution: needsinfo =>
Comment:
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: [https://docs.djangoproject.com/en/2.2/topics/i18n/translation
/#message-files message-files])
The project will raise the error: {{{ ValueError: 'Value' is not a valid
EnumClass }}}, on the generated migration file.
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:3>
* stage: Unreviewed => Accepted
Comment:
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.
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:4>
* status: new => assigned
* owner: nobody => Hasan Ramezani
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:5>
Comment (by Hasan Ramezani):
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?
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:6>
Comment (by felixxm):
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
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:7>
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:8>
* needs_tests: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:9>
* needs_tests: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:10>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:11>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"f0adf3b9b7a19cdee05368ff0c0c2d087f011180" f0adf3b9]:
{{{
#!CommitTicketReference repository=""
revision="f0adf3b9b7a19cdee05368ff0c0c2d087f011180"
Fixed #30774 -- Made serialization in migrations use members names for
Enums.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:12>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"df0c2ac3586a80a5e37cca3bcb2d4e7ed333d28e" df0c2ac3]:
{{{
#!CommitTicketReference repository=""
revision="df0c2ac3586a80a5e37cca3bcb2d4e7ed333d28e"
[3.0.x] Fixed #30774 -- Made serialization in migrations use members names
for Enums.
Backport of f0adf3b9b7a19cdee05368ff0c0c2d087f011180 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/30774#comment:13>