Как использовать пользовательские метки из поля 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, потому что он не ожидает кортежей.

Вернуться на верх