通过选项设置Django IntegerField名称
当你在模型字段中使用选项时,通常会有一些神奇的值和人类可读的名称对应起来。在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