Python及其最佳实践Django常量

时间:2012-10-10 15:20:09

标签: python django

我有一个依赖于元组的Django模型。我想知道在我的Django程序中引用该元组中的常量的最佳实践是什么。例如,在这里,我想将“default=0”指定为更具可读性且不需要评论的内容。有什么建议吗?

Status = (
    (-1, 'Cancelled'),
    (0, 'Requires attention'),
    (1, 'Work in progress'),
    (2, 'Complete'),
)

class Task(models.Model):
    status = models.IntegerField(choices=Status, default=0) # Status is 'Requires attention' (0) by default.

编辑:

如果可能的话,我想完全避免使用一个号码。不知何故,使用字符串'需要注意'会更具可读性。

10 个答案:

答案 0 :(得分:57)

定义整数值的常量非常常见,如下所示:

class Task(models.Model):
    CANCELLED = -1
    REQUIRES_ATTENTION = 0
    WORK_IN_PROGRESS = 1
    COMPLETE = 2

    Status = (
        (CANCELLED, 'Cancelled'),
        (REQUIRES_ATTENTION, 'Requires attention'),
        (WORK_IN_PROGRESS, 'Work in progress'),
        (COMPLETE, 'Complete'),
    )

    status = models.IntegerField(choices=Status, default=REQUIRES_ATTENTION)

通过移动类中的常量和Status,可以使模块的命名空间更加清晰,作为奖励,您可以在导入Tasks.COMPLETE模型的任何地方引用Tasks

答案 1 :(得分:16)

CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
Status = (
    (CANCELED, 'Cancelled'),
    (ATTENTION, 'Requires attention'),
    (WIP, 'Work in progress'),
    (COMPLETE, 'Complete'),
)

class Task(models.Model):
    status = models.IntegerField(choices=Status, default=CANCELED)

<小时/> 请记住,正如其他人所说,正确的方法是放置这些变量 在你的Model类中。这也是官方django example如何做到的。

只有一个原因,你想把它放在类命名空间之外 并且只有当这些语义由您的应用程序的其他模型平均共享时。即 你不能决定他们属于哪个特定的模型。

虽然在您的特定示例中似乎不是这种情况。

答案 2 :(得分:10)

Python 3.4+:Enum

你写&#34;如果可能的话,我想完全避免使用号码。&#34; 事实上,一个命名的代表显然更加pythonic。 但是,裸字符串容易出现拼写错误。

Python 3.4引入了一个名为的模块 enum提供EnumIntEnum个伪类 这有助于解决这种情况。 有了它,您的示例可以如下工作:

# in Python 3.4 or later:
import enum  

class Status(enum.IntEnum):
    Cancelled = -1,
    Requires_attention = 0,
    Work_in_progress = 1,
    Complete = 2

def choiceadapter(enumtype):
    return ((item.value, item.name.replace('_', ' ')) for item in enumtype)

class Task(models.Model):
    status = models.IntegerField(choices=choiceadapter(Status), 
                                 default=Status.Requires_attention.value)

一旦Django团队拿起Enum,就可以了 choiceadapter甚至可以构建到Django中。

答案 3 :(得分:7)

你可以使用namedtuple,使用一个常量似乎适合的永恒。 ; - )

>>> from collections import namedtuple
>>> Status = namedtuple('Status', ['CANCELLED', 'REQUIRES_ATTENTION', 'WORK_IN_PROGRESS', 'COMPLETE'])(*range(-1, 3))
>>> Status
Status(CANCELLED=-1, REQUIRES_ATTENTION=0, WORK_IN_PROGRESS=1, COMPLETE=2)
>>> Status.CANCELLED
-1
>>> Status[0]
-1

Task中使用__repr__上的常量作为Alasdair's answer中的常量在这种情况下更有意义,但是namedtuples是非常便宜的替代词和不会改变的对象。如果你想在内存中拥有 lot ,那就特别方便了。它们就像常规元组一样,具有描述性{{1}}和属性访问权。

答案 4 :(得分:3)

一种可能的方法是结合使用 tuple 的python range 函数。

class Task(models.Model):
    CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
    Status = (
        (CANCELLED, 'Cancelled'),
        (REQUIRES_ATTENTION, 'Requires attention'),
        (WORK_IN_PROGRESS, 'Work in progress'),
        (COMPLETE, 'Complete'),
    )

    status = models.IntegerField(choices=Status, default=REQUIRES_ATTENTION)

答案 5 :(得分:2)

我的方法:

class Task(models.Model):
    STATUSES = { 'cancelled': 'Cancelled',
                 'requires attention': 'Requires attention',
                 'work in progress': 'Work in progress',
                 'complete': 'Complete' }

    status = models.CharField(choices=STATUSES.items(), default='cancelled')

这允许您编写方便的表达式:

tasks = Task.objects.filter(status='complete')

此外,它允许您不创建不必要的全局变量。

如果你真的想使用整数字段:

class Task(models.Model):

   class STATUS:
      CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
      choices = {
        CANCELED: 'Cancelled',
        ATTENTION: 'Requires attention',
        WIP: 'Work in progress',
        COMPLETE: 'Complete'
      }


   status = models.CharField(choices=STATUSES.choices.items(), default=STATUSES.CANCELED)

tasks = Task.objects.filter(status=Task.STATUSES.COMPLETE)

答案 6 :(得分:1)

你可以使用字典来提高清晰度:

Status = {
    -1: 'Cancelled',
    0: 'Requires attention',
    1: 'Work in progress',
    2: 'Complete',
}

class Task(models.Model):
    status = models.IntegerField(choices=Status.items(), default=Status[0])

答案 7 :(得分:0)

我不使用Django,但是我在Pyramid和Twisted之下做了类似下面的事情......

def setup_mapping( pairs ):
    mapping = {'id':{},'name':{}}
    for (k,v) in pairs:
        mapping['id'][k]= v
        mapping['name'][v]= k
    return mapping

class ConstantsObject(object):
    _pairs= None
    mapping= None

    @classmethod
    def lookup_id( cls , id ):
       pass

    @classmethod
    def lookup_name( cls , name ):
       pass

class StatusConstants(ConstantsObject):
    CANCELLED = -1
    REQUIRES_ATTENTION = 0
    WORK_IN_PROGRESS = 1
    COMPLETE = 2

    _pairs= (
        (-1, 'Cancelled'),
        (0, 'Requires attention'),
        (1, 'Work in progress'),
        (2, 'Complete'),
    )
    mapping= setup_mapping(_pairs)

所以本质就是这样:

  • 有一个基本的“常量”类,每种类型都有另一个类。该类将关键字定义为ALLCAPS中的值
  • 我也把明文_pairs投入课堂。为什么?因为我可能需要用它们构建一些数据库表,或者我可能希望它们用于错误/状态消息。我使用数字而不是ALLCAPS变量名作为个人偏好。
  • 我初始化一个mapping类变量,它基本上通过在dict中预编译一堆变量来修改类,因为......
  • 该类派生自该基类,它提供了类方法功能来搜索值或执行常常需要对常量执行的其他标准操作。

这不是一种万能的方法,但我一般都非常喜欢这样。您可以轻松地使用dict来定义对,让'mapping'函数设置一些其他属性,例如将对值的元组赋予k,v或v,k或您可能需要的任何奇怪格式。

我的代码可能如下所示:

status_id = sa.Column(sa.Integer, sa.ForeignKey("_status.id") , nullable=False , default=constants.StatusConstants.CANCELLED )

status_name = constants.StatusConstants.lookup_id(status_id)    
status_name = constants.StatusConstants.mapping['id'][status_id]

无论何时需要以其他方式使用常量,只需添加或更改基类的类方法即可。

答案 8 :(得分:0)

有时我必须创建一些巨大的选择列表。我不喜欢像猴子一样打字,所以我宁愿创造一个这样的功能:

def choices(labels):
    labels = labels.strip().split('\n')
    ids = range(1, len(labels)+1)
    return zip(ids, labels)

并像这样使用:

my_choices = """
choice1
choice2
choice3
"""
MY_CHOICES = choices(my_choices)
print(MY_CHOICES) # ((1, choice1), (2, choice2), (3, choice3))

答案 9 :(得分:0)

Final ly常量已通过https://github.com/python/mypy/pull/5522添加到python中

安装:

pip install mypy typing_extensions

用法示例:

from typing_extensions import Final

DAYS_IN_A_WEEK: Final = 7
DAYS_IN_A_WEEK = 8  # I really want more days in a week!

您需要运行mypy类型检查器:

mypy --python-version=3.6 --strict week.py
week.py:4: error: Cannot assign to final name "DAYS_IN_A_WEEK"

有关更多信息,请参见:https://dev.to/wemake-services/1-minute-guide-to-real-constants-in-python-2bpk