如何在Django模型中将枚举用作选择字段

时间:2019-02-21 08:39:29

标签: python django python-3.x django-models enums

我有一个模型类,我希望将两个字段作为选择字段,因此为了填充这些选择,我使用了下面列出的枚举

#models.py
class Transaction(models.Model):
    trasaction_status = models.CharField(max_length=255, choices=TransactionStatus.choices())
    transaction_type = models.CharField(max_length=255, choices=TransactionType.choices())

#enums.py
class TransactionType(Enum):

    IN = "IN",
    OUT = "OUT"

    @classmethod
    def choices(cls):
        print(tuple((i.name, i.value) for i in cls))
        return tuple((i.name, i.value) for i in cls)

class TransactionStatus(Enum):

    INITIATED = "INITIATED",
    PENDING = "PENDING",
    COMPLETED = "COMPLETED",
    FAILED = "FAILED"
    ERROR = "ERROR"

    @classmethod
    def choices(cls):
        print(tuple((i.name, i.value) for i in cls))
        return tuple((i.name, i.value) for i in cls)

但是,当我尝试通过管理员访问此模型时,出现以下错误:

Django Version: 1.11
Exception Type: ValueError
Exception Value:    
too many values to unpack (expected 2)

我关注了以下两篇使用枚举的文章:

https://hackernoon.com/using-enum-as-model-field-choice-in-django-92d8b97aaa63

https://blog.richard.do/2014/02/18/how-to-use-enums-for-django-field-choices/

8 个答案:

答案 0 :(得分:10)

Django 3.0内置了对枚举的支持

示例:

from django.utils.translation import gettext_lazy as _

class Student(models.Model):

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

这些工作类似于Python标准库中的enum,但进行了一些修改:

  • 枚举成员值是构造具体数据类型时要使用的参数的元组。 Django支持在此元组的末尾添加一个额外的字符串值,以用作人类可读的名称,即labellabel可以是懒惰的可翻译字符串。因此,在大多数情况下,成员值将为(value, label)两元组。如果未提供元组,或者最后一项不是(惰性)字符串,则标签为成员名称中的automatically generated
  • 在值上添加.label属性,以返回人类可读的名称。 大量自定义属性已添加到枚举类– .choices.labels.values.names –使访问访问列表中这些单独部分的列表更加容易列举。使用.choices作为合适的值,以传递到字段定义中的选择。
  • 强制使用enum.unique()以确保不能多次定义值。在字段选择中这不太可能达到预期。

有关更多信息,请check the documentation

答案 1 :(得分:2)

如果您收到此错误:

<块引用>

'choices' 必须是一个包含(实际值,可读名称)元组的可迭代对象

并且正在使用 Django3,那么您可能遇到了与我相同的问题:“枚举”必须嵌入到您尝试使用它们的模型中,并且不能在模型之外声明。例如,这将不起作用:

class YearInSchool(models.TextChoices):
    FRESHMAN = 'FR', _('Freshman')
    SOPHOMORE = 'SO', _('Sophomore')
    JUNIOR = 'JR', _('Junior')
    SENIOR = 'SR', _('Senior')
    GRADUATE = 'GR', _('Graduate')

class Student(models.Model):
   year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

文档中的这个示例将:

class Student(models.Model):

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

答案 2 :(得分:0)

您的代码中的问题是response($.map(data.data, function (item, i) { //alert(item.value); return { id: item.id, label: item.label, value: item.value } }) ); 选项和其他选项之后的INITIATED = "INITIATED",是逗号。当我们在任何字符串后添加逗号时,它将成为一个元组。参见下面的示例

INITIATED

models.py

s = 'my str'
print(type(s))
# output: str

s = 'my str',
print(type(s))
# output: tuple

enums.py

class Transaction(models.Model):
    trasaction_status = models.CharField(max_length=255, choices=TransactionStatus.choices())
    transaction_type = models.CharField(max_length=255, choices=TransactionType.choices())

答案 3 :(得分:0)

您可以按照here所述设置各种选项来定义Enum

class TransactionStatus(Enum):

    INITIATED = "INITIATED"
    PENDING = "PENDING"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"
    ERROR = "ERROR"

注意没有逗号!这样一来,您便可以在以后的代码中引用TransactionStatus.ERRORTransactionStatus.PENDING

其余的代码是正确的。您可以通过创建choicesoption.name的元组来获得option.value

答案 4 :(得分:0)

顺便说一句,Djanog还支持Python 3的auto()作为Enum值。您可以使用以下帮助程序类来简化生活。

from django.db.models.enums import TextChoices

class AutoEnumChoices(TextChoices):
    def _generate_next_value_(name, start, count, last_values):  # @NoSelf
        return name.lower()
    
    @property
    def choices(cls):  # @NoSelf
        empty = [(None, cls.__empty__)] if hasattr(cls, '__empty__') else []
        return empty + [(member.value, member.label) for member in cls]

然后在您的选择定义中使用它:

class TransferBasicStatus(AutoEnumChoices):
    NONE = auto()
    WAITING = auto()
    PENDING = auto()
    PROGRESS = auto()
    SUCCESS = auto()
    DECLINED = auto()
    ENDED =  'ended', _('Ended - The transfer has ended with mixed states')

答案 5 :(得分:0)

您可以尝试根据 examples from docs. 执行类似操作:

from enum import Enum

class BaseEnum(Enum):
    def __new__(cls, *args):
        obj = object.__new__(cls)
        obj._value_ = args[0]
        obj.display_name = args[1]
        return obj

    @classmethod
    def model_choices(cls):
        return [(cls.__members__[member].value, cls.__members__[member].display_name)
            for member in cls.__members__.keys()]

这将导致:

>>> class TransactionType(BaseEnum):
...     IN = ('in', 'In')
...     OUT = ('out', 'Out')
...
>>> TransactionType.IN.value
'in'
>>> TransactionType.IN.display_name
'In'
>>> TransactionType.model_choices()
[('in', 'In'), ('out', 'Out')]

可以用作字段选择的参数。

答案 6 :(得分:0)

class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

对于 Django 3.0 以上,你可以使用上面的例子。

对于整数选择,您可以使用以下代码。

class Suit(models.IntegerChoices):
        DIAMOND = 1
        SPADE = 2
        HEART = 3
        CLUB = 4

    suit = models.IntegerField(choices=Suit.choices)

答案 7 :(得分:-1)

根据您对https://hackernoon.com/using-enum-as-model-field-choice-in-django-92d8b97aaa63的引用。选择应该是元组列表,而您将返回元组的元组。 我的更多信息与i.name不同。试试:

#enums.py
class TransactionType(Enum):

    IN = "IN",
    OUT = "OUT"

    @classmethod
    def choices(cls):
        return [(i, i.value) for i in cls]