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


70

我在这里阅读教程: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处于名为People的模型中,因此我提供的代码将在数据库中创建名为“ People”的“ Months Choices”列,并说明用户单击月份后出生的月份。并提交表格?


1
添加这一点,我建议您研究Django-Choices软件包。
约瑟夫·维克多·扎米特

Answers:


51

根据文档

现场选择

本身由恰好两个项目(例如[[(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')

160

我认为实际上没有人回答第一个问题:

他们为什么创建这些变量?

这些变量不是严格必需的。这是真的。您可以完美地执行以下操作:

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

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

为什么使用变量更好?错误预防和逻辑分离。

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模型字段中用于选择的命名空间变量;显然,该变量与特定字段相关,以避免使将来可能将类似选择字段添加到模型的程序员感到困惑。在python 3中,我为此使用了一个IntEnum类,并以一种使其与哪个模型字段相关的方式显而易见的方式对其进行命名。
亚当

34

对于Django3.0 +,请使用models.TextChoices(有关枚举类型,请参阅docs-v3.0

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
    )

1
对于较新版本的Django来说,这是一个很好的解决方案-具有额外的好处,TextChoices也可以将其定义为主类并在模型之间共享。永不更改数据的好选择。
Hendrik F


4

您的代码中不能有空话,这就是它们创建变量的原因(您的代码将以失败NameError)。

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

但是有更好的方法来创建所需的特定选择。请参阅前面的堆栈溢出问题

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

4

我建议使用django-model-utils而不是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类的标准成员)。

1

$ pip install django-better-choices

对于那些感兴趣的人,我创造了 django-better-choices了一个库,该库提供了一个不错的界面来与Django 3.7+版本一起使用。它支持自定义参数,许多有用的功能,并且对IDE非常友好。

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

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 Migrations完全支持它:

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

此处的完整文档:https : //pypi.org/project/django-better-choices/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.