我有一个依赖于元组的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.
编辑:
如果可能的话,我想完全避免使用一个号码。不知何故,使用字符串'需要注意'会更具可读性。
答案 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)
Enum
你写&#34;如果可能的话,我想完全避免使用号码。&#34; 事实上,一个命名的代表显然更加pythonic。 但是,裸字符串容易出现拼写错误。
Python 3.4引入了一个名为的模块
enum
提供Enum
和IntEnum
个伪类
这有助于解决这种情况。
有了它,您的示例可以如下工作:
# 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)
所以本质就是这样:
_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