How to use enums as a choice field in django model

Par*_*ras 8 python django enums django-models python-3.x

I have a model class of which I want two fields to be a choice fields, so to populate those choices I am using an enum as listed below

#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)
Run Code Online (Sandbox Code Playgroud)

However, when I am trying to access this model through admin I am getting the following error :

Django Version: 1.11
Exception Type: ValueError
Exception Value:    
too many values to unpack (expected 2)
Run Code Online (Sandbox Code Playgroud)

I followed two articles that described how to use enums:

anj*_*505 7

您的代码中的问题是选项和其他选项INITIATED = "INITIATED",后的逗号INITIATED。当我们在任何字符串后添加逗号时,它将成为一个元组。请参阅下面的示例

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

s = 'my str',
print(type(s))
# output: tuple
Run Code Online (Sandbox Code Playgroud)

模型.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())
Run Code Online (Sandbox Code Playgroud)

枚举.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)
Run Code Online (Sandbox Code Playgroud)


Ces*_*ssa 7

Django 3.0内置了对Enums的支持

例:

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,
    )
Run Code Online (Sandbox Code Playgroud)

这些工作类似于Python标准库中的枚举,但有一些修改:

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

有关更多信息,请查看文档

  • @DavidPiao 恐怕我不明白你的问题。但这里是“a”问题的答案:不幸的是,Django 永远不会返回枚举类型,并且总是返回字符串。因此 `foo.state == State.DRAFT` 将适用于字符串比较,但 Python 文档首选 `foo.state is State.DRAFT` 则不会。你可以做`State(foo.state) is State.DRAFT`。 (4认同)
  • 这里只是警告,“student.year_in_school”返回一个字符串,而不是一个枚举。您必须手动将结果转换回来或使用 ==/!= (2认同)
  • `return self.YearInSchool[self.year_in_school]` 应该有大括号而不是方括号:`return self.YearInSchool(self.year_in_school)`,否则你会得到一个 KeyError。 (2认同)

dir*_*ten 5

您可以Enum通过设置此处所述的各种选项来定义一个:

class TransactionStatus(Enum):

    INITIATED = "INITIATED"
    PENDING = "PENDING"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"
    ERROR = "ERROR"
Run Code Online (Sandbox Code Playgroud)

注意没有逗号!这使您稍后可以在代码中引用TransactionStatus.ERRORTransactionStatus.PENDING

您的其余代码是正确的。你得到choices通过创建的元组option.nameoption.value


rai*_*lin 5

我的项目的一个例子:

import enum

from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.translation import gettext_lazy as _


class NotificationTemplate(models.Model):
class Meta:
    verbose_name = _('notification template')
    verbose_name_plural = _('notification templates')

@enum.unique
class Name(str, enum.Enum):
    ONBOARDING = 'onboarding'
    TG_ERROR = 'tg_error'
    FB_ERROR = 'fb_error'

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

@enum.unique
class Type(int, enum.Enum):
    PUSH = 1
    EMAIL = 2
    TELEGRAM = 3
    VK = 4
    OTHER = 5

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

name = models.CharField(_('notification name'), max_length=64, unique=True, choices=Name.choices(), default=Name.ONBOARDING)
template_type = ArrayField(models.PositiveSmallIntegerField(_('type'), choices=Type.choices()))
max_count = models.PositiveSmallIntegerField(default=1)

def __str__(self):
    return self.Name(self.name).name
Run Code Online (Sandbox Code Playgroud)