【问题标题】:Enum using Django ModelField choices as string -- anti-pattern?使用 Django 模型字段选择作为字符串的枚举——反模式?
【发布时间】:2014-07-10 05:40:02
【问题描述】:

我有一个 Django 字段,基本上用作通知首选项的枚举。

现在我的设置是这样的:

class MyModel(models.Model):
    # ...
    EVERY_TIME = 'every'; WEEKLY = 'weekly'; NEVER = 'never'
    NOTIFICATION_CHOICES = ((EVERY_TIME, "Every time"), (WEEKLY, "Weekly"), (NEVER, "Never"))
    notification_preferences = models.CharField(choices=NOTIFICATION_CHOICES, default=EVERY_TIME, max_length=10)

我知道一般这种枚举应该设置为models.IntegerField而不是CharField,但是由于前端使用Angular并且数据都是通过API提供的,我觉得它可能例如,为前端接收'weekly' 而不是2 提供更多有用的信息。

使用CharField 作为枚举是否被认为是不好的做法?如果是这样,我的用例是否足够小以至于没什么大不了的,还是我缺少一些我应该改变它的东西?

【问题讨论】:

  • 更多 pythonic 对频率变量进行元组分配:EVERY_TIME, WEEKLY, NEVER = 'every', 'weekly', 'never'

标签: python django enums


【解决方案1】:

您可能需要考虑Django-model-utils Choices 库,它可以让您更好地控制枚举的文本版本。

要回答您的问题,并非所有内容都适合整数标识符。考虑州,澳大利亚有 7 个州,并且它们在已知时有一个固定的集合:

ACT - Australian Capital Territory
NSW - New South Wales
NT  - Northern Territory
QLD - Queensland
SA  - South Australia
TAS - Tasmania
VIC - Victoria
WA  - Western Australia

由于这些是相对固定的(国家的构成不太可能改变,所以没有理由为每个分配一个整数,当文本编码和全名也一样。

我不会说使用CharField 作为选择是一种反模式,只是当您确定数据库的缩写版本在存储为文本时有意义时才应该应用这种方法。

您也可以使用Enum 来存储您的值

from enum import Enum

class Countries(Enum):
   ACT = "Australian Capital Territory"
   NSW = "New South Wales"
   ...

class MyModel(models.Model):
    country = models.CharField(choices=[(tag.name, tag.value) for tag in Countries])

【讨论】:

  • 很遗憾,我不能把它归功于它,但它很棒 - 并且实际上是必不可少的。
  • 不幸的是,这并没有回答我的问题,这更多的是关于效率,因为Choice 仍然是一个很棒的工具。看起来他们有例子,虽然选择为Charfield,所以我想这就是我的答案。
  • @jdotjdot 我添加了一些应该回答问题的上下文。
  • 谢谢;你非常乐于助人。
【解决方案2】:

将 CharField 用作枚举是否被认为是不好的做法?如果是这样,是 我的用例足够小,没什么大不了的,或者在那里 我缺少的东西我应该改变它吗?

你看这个完全错误的方式。字段的选择完全取决于您要存储的数据。所以,问题不在于CharField 是否对枚举不好,您需要问的问题是:

“我需要在我的数据库中存储什么 信息?”

有一种观点认为,如果您希望数据库帮助您对该字段进行计算,您应该只在数据库中存储整数,否则,您应该将数字存储为字符串类型,因为这样可以为您提供更多灵活性。

我不会对此进行更深入的讨论,因为这是一个固执己见的话题,我要说的是,如果您认为将数据存储为字符对您的应用程序有意义,那么就去做吧。不要在脑海中煮过头。

我知道一般这种枚举应该设置为 models.IntegerField 而不是 CharField,但由于前端 使用 Angular 并且数据都是通过 API 提供的,我觉得 可能会为前端提供更多有用的信息 例如,接收“每周”而不是 2。

我不确定您是从哪里得出结论的,但是对于枚举应该引用什么数据类型并没有硬性规定。这取决于您的应用程序以及您需要存储哪些信息 - 并决定您需要知道如何查询数据库以获取此信息。

在您的情况下,您是从 API 可用性的角度来看待这个问题的,这非常好。您使用此模型的主要目的是存储信息,并且信息的存储方式应为您的应用程序提供最实用的功能。

最后,不要在 Python 中使用;。虽然技术上没有错误,但这通常是不受欢迎的,不符合python style guide

【讨论】:

  • 关于 ;,虽然我很清楚 Python 样式指南所说的内容,但对于像这样的一小部分选择任务,我喜欢使用 ;,它是唯一的我做的时间。当您将每个选择分配放在自己的行上时,它最终会占用模型中的大量空间,并且它必须在相关字段之前声明,因此我不能将它们全部粘贴在底部,它使我的模型比我喜欢的要笨重——所以我(和我见过的其他人)就是这样做的。
【解决方案3】:
【解决方案4】:

考虑到最新的 Django 文档在its choices example 中推荐了这种方法,在此转载:

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) 

【讨论】:

    【解决方案5】:
    猜你喜欢
    • 2019-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-03
    • 2016-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多