django models choices的三种实现方式

在Django官方文档中,它们提供了2个关于如何使用预定义选项作为特定字段选择的示例

官方文档的例子

第一个例子是定义choices用一个元组包括的元组, 每个元组都是外边元组的值。
里面元组的第一个元素的值是设置在modle的值, 第二个元素的值是它的字符串表示。

1
2
3
4
5
6
7

YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
)

另一个例子, 官方建议我们定义选项为model类的常量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

from django.db import models
class (models.Model):
# Constants in Model class
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
YEAR_IN_SCHOOL_CHOICES = (
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
)
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)

可以很明显的看出, 第二种方式是开发友好的。

我的首选方式 - 使用枚举

第三种选择是我在模型中定义字段选择的首选方法 - 枚举

什么是枚举

枚举是Python标准库的内容之一
我们先来看官方定义:

An enumeration is a set of symbolic names (members) bound to unique, constant values. Within an enumeration, the members can be compared by identity, and the enumeration itself can be iterated over.
翻译一下就是:
“枚举是一组绑定到唯一常量值的符号名称(成员)。在枚举中,可以通过标识来比较成员,并且可以迭代枚举本身。“
有兴趣的同学可以看一遍它的
官方文档

定义枚举

在这篇文章中,我将通过一个简单的例子引导您使用Enum作为字段的选择。假设有一个字段来识别一本书的语言,语言的选择是英语,德语,西班牙语和中文。
首先,我将定义一个为这些所有的选项定义一个类:

1
2
3
4
5
6
from enum import Enum
class LanguageChoice(Enum): # 枚举类的子类 实际上Enum是一个元类 继承自所有类的元类 type
DE = "German"
EN = "English"
CN = "Chinese"
ES = "Spanish"

然后,我将在modle类下面做如下定义:

1
2
3
4
5
6
7
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=255)
language = models.CharField(
max_length=5,
choices=[(tag.name, tag.value) for tag in LanguageChoice] # 选项是一个元组列表
)

在我们的Book模型下, 语言的选项是一个又元组构成的列表, 为了简单起见, 我们用了列表生成式生成我们的列表, 其实choices接收一个可迭代对象, 我们也可以把[]变成()那它就变成一个生成式表达式了。
对于选择中的每个元组,第一个元素是存储到模型中的值,而第二个元素是其人类可读的表示。

使用枚举

现在我们已经定义了我们的枚举和模型,我们将如何使用它?假设我们要添加一个语言为’CH’的Book条目:

1
2
3
# import your Models and Enums
b = Book(title='流畅的Python', language=LanguageChoice.CH.name)
b.save()

请注意,我们如何为参数语言提供值取决于我们在选择列表的元组中定义的第一个元素。我们使用(tag.name,tag.value)标记为Enum类型。

为什么要用枚举

您可能会问,“为什么使用Enum而不是Django官方文档中给出的示例?”

  1. 与值集中的任何其他值不同。例如,我们可以简单地定义一周中几天的整数1-7,那么我们有另一组值,它怎么表示包括1-7的子集?
  2. 预定义值在内存中,Enumeration可用于整个程序,而不仅仅是您的模型中。

什么时候使用Enum?

根据PEP 435,枚举对于定义可能具有或不具有语义含义的不可变的,相关的常量值集合是有用的。
经典的例子是一周中的几天(周日到周六)和学校评估等级(’A’到’D’和’F’)。
其他示例包括错误状态值和定义过程中的状态。

附 更加优雅的实现方式 重写元类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__author__ = 'Cluas'

from enum import Enum, EnumMeta


class ChoiceEnumMeta(EnumMeta):
def __iter__(self):
return ((tag, tag.value) for tag in super().__iter__())


class ChoiceEnum(Enum, metaclass=ChoiceEnumMeta):
"""
Enum for Django ChoiceField use.

Usage::
class Languages(ChoiceEnum):
ch = "Chinese"
en = "English"
fr = "French"
class MyModel(models.Model):
language = models.CharField(max_length=20, choices=Colors)
"""

Github gist: click > 代码地址

本文到此结束, 希望对同学们有所帮助!