如何正确使用Django中的"选项"字段选项

use*_*875 41 python django django-models

我正在阅读这里的教程: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)
Run Code Online (Sandbox Code Playgroud)

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

FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
Run Code Online (Sandbox Code Playgroud)

他们为什么要创建这些变量?此外,MONTHS_CHOICES在一个名为People的模型中,因此我提供的代码会在数据库中创建一个名为"People"的"月份选择"列,并且它会说明用户在点击几个月后出生的月份并提交表格?

JCJ*_*CJS 105

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

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

这些变量并非绝对必要.这是真的.你可以完美地做这样的事情:

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

month = models.CharField(max_length=9,
                  choices=MONTH_CHOICES,
                  default="JANUARY")
Run Code Online (Sandbox Code Playgroud)

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

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

MONTH_CHOICES = (
    (JAN, "January"),
    (FEB, "February"),
    (MAR, "March"),
    # ....
    (DEC, "December"),
)
Run Code Online (Sandbox Code Playgroud)

现在,假设您有一个视图,您可以在其中创建新的Model实例.而不是这样做:

new_instance = MyModel(month='JANUARY')
Run Code Online (Sandbox Code Playgroud)

你会这样做的:

new_instance = MyModel(month=MyModel.JAN)
Run Code Online (Sandbox Code Playgroud)

在第一个选项中,您将对该值进行硬编码.如果您可以输入一组值,则应在编码时限制这些选项.此外,如果您最终需要更改Model层的代码,现在您无需在Views层中进行任何更改.

  • 从 python3.8 开始,在声明 `MONTH_CHOICES` 中声明变量可能会更方便,例如 `MONTH_CHOICES = ((JAN := "JANUARY", "January")...)`。 (7认同)

Wak*_*eng 54

对于Django3.0 +,使用models.TextChoices(见文档-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
    )
Run Code Online (Sandbox Code Playgroud)

用法::

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
    )
Run Code Online (Sandbox Code Playgroud)

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

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

>>> obj2 = MyModel(month=MyModel.Month.FEB)
>>> assert obj2.get_month_display() == obj2.Month(obj2.month).label
Run Code Online (Sandbox Code Playgroud)

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

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'
Run Code Online (Sandbox Code Playgroud)

  • 请注意,您可以简单地使用“JANUARY = 1”、“FEBRUARY = 2”等,而不指定标签,并且 Django 足够智能,可以从选择选项列表中生成“January”、“February”等。 (6认同)
  • 对于较新版本的 Django 来说,这是一个很好的解决方案 - 额外的好处是“TextChoices”也可以定义为主类并在模型之间共享。永远不会改变数据的好选择。 (2认同)

ale*_*cxe 32

根据文件:

Field.choices

一个可迭代的(例如,一个列表或元组),它由正好两个项目的迭代组成(例如[(A,B),(A,B)...]),用作该字段的选择.如果给出了此选项,则默认表单窗口小部件将是具有这些选项的选择框,而不是标准文本字段.

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

所以,你的代码是正确的,但您应该定义变量JANUARY,FEBRUARY等,或使用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')
Run Code Online (Sandbox Code Playgroud)


小智 16

2023 年 1 月更新:

\n

新方法是使用models.TextChoices ,如下所示,它是内置的,因此您不需要安装任何软件包。*第一个值是实际值,第二个值显示在 Django Admin 上:

\n
# "models.py"\n\nfrom django.db import models\n\nclass MyModel(models.Model):\n\n    class Months(models.TextChoices):\n     # Actual value \xe2\x86\x93      # \xe2\x86\x93 Displayed on Django Admin  \n        JANUARY = \'JAN\', \'January\'\n        FEBRUARY = \'FEB\', \'February\'\n        MARCH = \'MAR\', \'March\'\n        APRIL = \'APR\', \'April\'\n        MAY = \'MAY\', \'May\'\n\n    month = models.CharField(\n        max_length=3,\n        choices=Months.choices,\n        default=Months.APRIL \n    )\n\n    class YearInSchool(models.TextChoices):\n     # Actual value \xe2\x86\x93      # \xe2\x86\x93 Displayed on Django Admin\n        FRESHMAN = \'FR\', \'Freshman\'\n        SOPHOMORE = \'SO\', \'Sophomore\'\n        JUNIOR = \'JR\', \'Junior\'\n        SENIOR = \'SR\', \'Senior\'\n        GRADUATE = \'GR\', \'Graduate\'\n\n    year_in_school = models.CharField(\n        max_length=2,\n        choices=YearInSchool.choices,\n        default=YearInSchool.SOPHOMORE,\n    )\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
# "models.py"\n\nfrom django.db import models\n\nclass MyModel(models.Model):\n              # \xe2\x86\x93 Actual value\n    JANUARY = \'JAN\'\n    FEBRUARY = \'FEB\'\n    MARCH = \'MAR\'\n    APRIL = \'APR\'\n    MAY = \'MAY\'\n\n    MONTHS = [      # \xe2\x86\x93 Displayed on Django Admin\n        (JANUARY, \'January\'),\n        (FEBRUARY, \'February\'),\n        (MARCH, \'March\'),\n        (APRIL, \'April\'),\n        (MAY, \'May\'),\n    ]\n\n    month = models.CharField(\n        max_length=3,\n        choices=MONTHS,\n        default=APRIL # Or "default=MONTHS[3]"\n    )\n              # \xe2\x86\x93 Actual value\n    FRESHMAN = \'FR\'\n    SOPHOMORE = \'SO\'\n    JUNIOR = \'JR\'\n    SENIOR = \'SR\'\n    GRADUATE = \'GR\'\n\n    YEAR_IN_SCHOOL_CHOICES = [\n                     # \xe2\x86\x93 Displayed on Django Admin\n        (FRESHMAN, \'Freshman\'),\n        (SOPHOMORE, \'Sophomore\'),\n        (JUNIOR, \'Junior\'),\n        (SENIOR, \'Senior\'),\n        (GRADUATE, \'Graduate\'),\n    ]\n\n    year_in_school = models.CharField(\n        max_length=2,\n        choices=YEAR_IN_SCHOOL_CHOICES,\n        default=SOPHOMORE # Or "default=YEAR_IN_SCHOOL_CHOICES[1]"\n    )\n
Run Code Online (Sandbox Code Playgroud)\n


Bab*_*yan 10

最干净的解决方案是使用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)
Run Code Online (Sandbox Code Playgroud)

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

  • 嗯..我个人不太愿意为如此简单的任务安装库。看看@Waket Cheng 的回答 (2认同)

dta*_*tar 5

我建议使用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,
   )
Run Code Online (Sandbox Code Playgroud)

并使用 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,
   )
Run Code Online (Sandbox Code Playgroud)
  • 这种方法有一个小缺点:在任何 IDE(例如 PyCharm)中,对于可用选项都没有代码补全(这是因为这些值不是 Choices 类的标准成员)。