Как использовать пользовательские метки из поля Django models.Choices вместе с enum.auto?
До Django v3.0 я использовал enum.Enum вместе с enum.auto ( см. ссылку) для полей выбора. Причина была в том, что я мог использовать класс перечисления для подсказок типов, а auto принудительно использовал класс перечисления вместо использования константного значения в коде. Однако в Django v3.0+ появился модуль enumeration types, который похож на модуль enum, но с некоторыми интересными возможностями. Я хотел бы использовать его вместе с enum.auto, но пока не смог.
Дальше я дошел до чего-то вроде этого:
class MyChoices(models.IntegerChoices, Enum):
FIRST = auto()
SECOND = auto()
THIRD = auto()
Значения перечислений и автоматически созданные метки верны:
MyChoices
Out[13]: <enum 'MyChoices'>
MyChoices.choices
Out[14]: [(1, 'First'), (2, 'Second'), (3, 'Third')]
MyChoices.labels
Out[15]: ['First', 'Second', 'Third']
MyChoices.values
Out[16]: [1, 2, 3]
MyChoices.FIRST
Out[17]: <MyChoices.FIRST: 1>
Проблема возникает, когда я пытаюсь определить пользовательские метки:
class MyChoices(models.IntegerChoices, Enum):
FIRST = auto(), '1st'
SECOND = auto(), '2nd'
THIRD = auto(), '3rd'
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3441, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-20-cf882f261e5f>", line 1, in <module>
class MyChoices(models.IntegerChoices, Enum):
File "/usr/local/lib/python3.8/site-packages/django/db/models/enums.py", line 28, in __new__
cls = super().__new__(metacls, classname, bases, classdict, **kwds)
File "/usr/local/lib/python3.8/enum.py", line 219, in __new__
enum_member = __new__(enum_class, *args)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'auto'
Я предполагаю, что проблема в том, что что-то конфликтует с унаследованным классом Enum. Я пробовал удалить его, но тогда auto() не работает должным образом. Есть ли у кого-нибудь идеи, как заставить его работать?
Я обнаружил, что это невозможно без патча к типу перечисления Django. Описанное выше исключение возникает потому, что значение auto не решено к моменту вызова этого куска кода, поэтому он пытается использовать int для преобразования экземпляра auto. Это происходит только тогда, когда перечисление является кортежем, т.е. auto() и меткой, из-за этой части кода в _EnumDict.__setitem__ (модуль enum):
elif not _is_descriptor(value):
if key in self:
# enum overwriting a descriptor?
raise TypeError('%r already defined as: %r' % (key, self[key]))
if isinstance(value, auto):
if value.value == _auto_null:
value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:])
self._auto_called = True
value = value.value
Это нормально, когда перечисление только auto, но не работает, когда объявляется вместе с меткой, потому что value - это кортеж. Это не проблема самого модуля enum, потому что он не ожидает кортежей.