通过选项设置Django IntegerField名称

144 投票
10 回答
121875 浏览
提问于 2025-04-15 12:51

当你在模型字段中使用选项时,通常会有一些神奇的值和人类可读的名称对应起来。在Django中,有没有什么方便的方法可以通过人类可读的名称来设置这些字段,而不是使用值呢?

考虑这个模型:

class Thing(models.Model):
  PRIORITIES = (
    (0, 'Low'),
    (1, 'Normal'),
    (2, 'High'),
  )

  priority = models.IntegerField(default=0, choices=PRIORITIES)

在某个时刻,我们有一个Thing实例,我们想要设置它的优先级。显然,你可以这样做:

thing.priority = 1

但这样的话,你就得记住优先级的值和名称之间的对应关系。这种方式是行不通的:

thing.priority = 'Normal' # Throws ValueError on .save()

目前我有这个笨拙的解决办法:

thing.priority = dict((key,value) for (value,key) in Thing.PRIORITIES)['Normal']

但这样做太麻烦了。考虑到这种情况可能很常见,我在想有没有人有更好的解决方案。有没有什么方法可以通过选择的名称来设置字段,而我完全没有注意到呢?

10 个回答

8

我很喜欢这种不断定义的方式,但我认为枚举(Enum)类型在这个任务中更好。它们可以同时表示一个项目的整数和字符串,同时让你的代码更易读。

枚举是在Python 3.4版本中引入的。如果你使用的是更低的版本(比如2.x),你仍然可以通过安装回溯包来使用它:pip install enum34

# myapp/fields.py
from enum import Enum    


class ChoiceEnum(Enum):

    @classmethod
    def choices(cls):
        choices = list()

        # Loop thru defined enums
        for item in cls:
            choices.append((item.value, item.name))

        # return as tuple
        return tuple(choices)

    def __str__(self):
        return self.name

    def __int__(self):
        return self.value


class Language(ChoiceEnum):
    Python = 1
    Ruby = 2
    Java = 3
    PHP = 4
    Cpp = 5

# Uh oh
Language.Cpp._name_ = 'C++'

这差不多就是全部了。你可以继承ChoiceEnum来创建自己的定义,并在模型定义中使用它们,比如:

from django.db import models
from myapp.fields import Language

class MyModel(models.Model):
    language = models.IntegerField(choices=Language.choices(), default=int(Language.Python))
    # ...

查询就像是锦上添花,你可以想象:

MyModel.objects.filter(language=int(Language.Ruby))
# or if you don't prefer `__int__` method..
MyModel.objects.filter(language=Language.Ruby.value)

将它们以字符串形式表示也变得简单:

# Get the enum item
lang = Language(some_instance.language)

print(str(lang))
# or if you don't prefer `__str__` method..
print(lang.name)

# Same as get_FOO_display
lang.name == some_instance.get_language_display()
45

从Django 3.0开始,你可以使用:

class ThingPriority(models.IntegerChoices):
    LOW = 0, 'Low'
    NORMAL = 1, 'Normal'
    HIGH = 2, 'High'


class Thing(models.Model):
    priority = models.IntegerField(default=ThingPriority.LOW, choices=ThingPriority.choices)

# then in your code
thing = get_my_thing()
thing.priority = ThingPriority.HIGH
191

按照这里的做法,你可以使用一个词来代表合适的整数值。

比如这样:

LOW = 0
NORMAL = 1
HIGH = 2
STATUS_CHOICES = (
    (LOW, 'Low'),
    (NORMAL, 'Normal'),
    (HIGH, 'High'),
)

这样在数据库里它们依然是整数。

使用时可以这样写:thing.priority = Thing.NORMAL

撰写回答