如何正确使用“选择”字段选项在 Django

我正在这里阅读教程: < a href = “ https://docs.djangoproject.com/en/1.5/ref/model/fields/# options”> https://docs.djangoproject.com/en/1.5/ref/models/fields/#choices 我正在创建一个框,用户可以选择他出生的月份。我试图

 MONTH_CHOICES = (
(JANUARY, "January"),
(FEBRUARY, "February"),
(MARCH, "March"),
....
(DECEMBER, "December"),
)


month = CharField(max_length=9,
choices=MONTHS_CHOICES,
default=JANUARY)

是这样吗?我看到在我正在阅读的教程中,出于某种原因,他们首先创建了变量,就像这样

FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'

他们为什么要创造这些变量?此外,MONTHS _ CHOICES 是在一个名为“人”的模型中,那么我提供的代码是否会在数据库中创建一个名为“人”的“月选择”列,并且在用户点击这些月份并提交表单之后会显示用户出生于哪个月份?

222953 次浏览

根据 文件:

Field.choices

可迭代的(例如,一个列表或元组) 正好有两个项(例如[(A,B) ,(A,B) ... ])的迭代 如果给定了这个选项,默认的表单小部件将 是一个包含这些选项的选择框,而不是标准文本字段。

每个元组中的第一个元素是要存储的实际值,并且 第二个元素是人类可读的名称。

因此,您的代码是正确的,除了您应该定义变量 JANUARYFEBRUARY等,或者使用 calendar模块来定义 MONTH_CHOICES:

import calendar
...


class MyModel(models.Model):
...


MONTH_CHOICES = [(str(i), calendar.month_name[i]) for i in range(1,13)]


month = models.CharField(max_length=9, choices=MONTH_CHOICES, default='1')

你不能在代码中只有单词,这就是为什么他们创建变量(你的代码将与 NameError失败)。

您提供的代码将创建一个名为 month的数据库表(再加上前缀 django) ,因为这是 CharField的名称。

但是有更好的方法来创建您想要的特定选择。

import calendar
tuple((m, m) for m in calendar.month_name[1:])

我认为没有人真正回答过第一个问题:

他们为什么要创造这些变量?

这些变量并不是必须的,这是真的,你完全可以这样做:

MONTH_CHOICES = (
("JANUARY", "January"),
("FEBRUARY", "February"),
("MARCH", "March"),
# ....
("DECEMBER", "December"),
)


month = models.CharField(max_length=9,
choices=MONTH_CHOICES,
default="JANUARY")

Why using variables is better? Error prevention and logic separation.

JAN = "JANUARY"
FEB = "FEBRUARY"
MAR = "MAR"
# (...)


MONTH_CHOICES = (
(JAN, "January"),
(FEB, "February"),
(MAR, "March"),
# ....
(DEC, "December"),
)

现在,假设您有一个视图,您可以在其中创建一个新的 Model 实例:

new_instance = MyModel(month='JANUARY')

你要这么做:

new_instance = MyModel(month=MyModel.JAN)

在第一个选项中,您硬编码的值。如果有一组值可以输入,那么在编写代码时应该限制这些选项。 此外,如果您最终需要更改模型层的代码,现在您不需要在视图层中进行任何更改。

最干净的解决方案是使用 django-model-utils库:

from model_utils import Choices


class Article(models.Model):
STATUS = Choices('draft', 'published')
status = models.CharField(choices=STATUS, default=STATUS.draft, max_length=20)

Https://django-model-utils.readthedocs.io/en/latest/utilities.html#choices

我建议使用 Django-模型-实用工具而不是 Django 内置的解决方案。 这种解决方案的主要优点是不存在字符串声明复制。所有选择项只声明一次。这也是使用3个值声明选项和存储与源代码中的用法不同的数据库值的最简单方法。

from django.utils.translation import ugettext_lazy as _
from model_utils import Choices


class MyModel(models.Model):
MONTH = Choices(
('JAN', _('January')),
('FEB', _('February')),
('MAR', _('March')),
)
# [..]
month = models.CharField(
max_length=3,
choices=MONTH,
default=MONTH.JAN,
)

使用 IntegerField 代替:

from django.utils.translation import ugettext_lazy as _
from model_utils import Choices


class MyModel(models.Model):
MONTH = Choices(
(1, 'JAN', _('January')),
(2, 'FEB', _('February')),
(3, 'MAR', _('March')),
)
# [..]
month = models.PositiveSmallIntegerField(
choices=MONTH,
default=MONTH.JAN,
)
  • 这种方法有一个小缺点: 在任何 IDE (例如。PyCharm)将不会有可用选项的代码完成(这是因为这些值不是 Choices 类的标准成员)。

For Django3.0+, use models.TextChoices (see docs-v3.0 for 枚举类型)

from django.db import models


class MyModel(models.Model):
class Month(models.TextChoices):
JAN = "1", "JANUARY"
FEB = "2", "FEBRUARY"
MAR = "3", "MAR"
# (...)


month = models.CharField(
max_length=2,
choices=Month.choices,
default=Month.JAN
)

用途:

>>> obj = MyModel.objects.create(month='1')
>>> assert obj.month == obj.Month.JAN == '1'
>>> assert MyModel.Month(obj.month) is obj.Month.JAN
>>> assert MyModel.Month(obj.month).value is '1'
>>> assert MyModel.Month(obj.month).label == 'JANUARY'
>>> assert MyModel.Month(obj.month).name == 'JAN'
>>> assert MyModel.objects.filter(month=MyModel.Month.JAN).count() >= 1


>>> obj2 = MyModel(month=MyModel.Month.FEB)
>>> assert obj2.get_month_display() == obj2.Month(obj2.month).label

假设我们知道标签是“ JANUARY”,那么如何获得名称“ JAN”和值“1”呢?

label = "JANUARY"
name = {i.label: i.name for i in MyModel.Month}[label]
print(repr(name))  # 'JAN'
value = {i.label: i.value for i in MyModel.Month}[label]
print(repr(value))  # '1'

就个人而言,我宁愿使用 models.IntegerChoices

class MyModel(models.Model):
class Month(models.IntegerChoices):
JAN = 1, "JANUARY"
FEB = 2, "FEBRUARY"
MAR = 3, "MAR"
# (...)


month = models.PositiveSmallIntegerField(
choices=Month.choices,
default=Month.JAN
)

$pip install django-better-options

For those who are interested, I have created django-better-choices library, that provides a nice interface to work with Django choices for Python 3.7+. It supports custom parameters, lots of useful features and is very IDE friendly.

您可以将您的选择定义为一个类:

from django_better_choices import Choices




class PAGE_STATUS(Choices):
CREATED = 'Created'
PENDING = Choices.Value('Pending', help_text='This set status to pending')
ON_HOLD = Choices.Value('On Hold', value='custom_on_hold')


VALID = Choices.Subset('CREATED', 'ON_HOLD')


class INTERNAL_STATUS(Choices):
REVIEW = 'On Review'


@classmethod
def get_help_text(cls):
return tuple(
value.help_text
for value in cls.values()
if hasattr(value, 'help_text')
)

然后执行以下操作和 远远不止:

print( PAGE_STATUS.CREATED )                # 'created'
print( PAGE_STATUS.ON_HOLD )                # 'custom_on_hold'
print( PAGE_STATUS.PENDING.display )        # 'Pending'
print( PAGE_STATUS.PENDING.help_text )      # 'This set status to pending'


'custom_on_hold' in PAGE_STATUS.VALID       # True
PAGE_STATUS.CREATED in PAGE_STATUS.VALID    # True


PAGE_STATUS.extract('CREATED', 'ON_HOLD')   # ~= PAGE_STATUS.VALID


for value, display in PAGE_STATUS:
print( value, display )


PAGE_STATUS.get_help_text()
PAGE_STATUS.VALID.get_help_text()

当然,它得到了 Django 和 Django 的完全支持:

class Page(models.Model):
status = models.CharField(choices=PAGE_STATUS, default=PAGE_STATUS.CREATED)

完整的文档在这里: https://pypi.org/project/django-better-choices/

2022年3月更新:

最简单、最容易、最好和最新的方法是使用 “模特,文本选择”,即 内置的,即 “不需要安装任何软件包”

“ models.py” :

from django.db import models


class MyModel(models.Model):


class Months(models.TextChoices):
JANUARY = 'JAN', 'January'
FEBRUARY = 'FEB', 'February'
MARCH = 'MAR', 'March'
APRIL = 'APR', 'April'
MAY = 'MAY', 'May'


month = models.CharField(
max_length=3,
choices=Months.choices,
default=Months.APRIL
)


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.SOPHOMORE,
)

我还用旧的方法重写了上面的代码,也就是 内置的

“ models.py” :

from django.db import models


class MyModel(models.Model):


JANUARY = 'JAN'
FEBRUARY = 'FEB'
MARCH = 'MAR'
APRIL = 'APR'
MAY = 'MAY'


MANTHS = [
(JANUARY, 'January'),
(FEBRUARY, 'February'),
(MARCH, 'March'),
(APRIL, 'April'),
(MAY, 'May'),
]


month = models.CharField(
max_length=3,
choices=MANTHS,
default=APRIL # or "default=MANTHS[4]"
)


FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
GRADUATE = 'GR'


YEAR_IN_SCHOOL_CHOICES = [
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
(GRADUATE, 'Graduate'),
]


year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=SOPHOMORE # or "default=YEAR_IN_SCHOOL_CHOICES[1]"
)

正如你所看到的,新方法比旧方法简单得多。