在 Django 中,如何为 IntegerChoices 枚举定义字符串值?
Posted
技术标签:
【中文标题】在 Django 中,如何为 IntegerChoices 枚举定义字符串值?【英文标题】:In Django, how do I define a string value for IntegerChoices enum? 【发布时间】:2021-12-23 11:03:07 【问题描述】:我正在使用 Django 3.2 和 Python 3.9。在我的模型中,我定义了一个 int 枚举。我也想为它定义可读的字符串值,所以我尝试了
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0
SELL = 1
labels =
BUY: 'Buy',
SELL: 'Sell'
translation = v: k for k, v in labels.items()
但此定义因错误而失败
TypeError: int() argument must be a string, a bytes-like object or a number, not 'dict'
如何为每个值定义字符串?我不介意字符串是否只是文字变量名称(例如“BUY”、“SELL”)
编辑:响应给出的答案之一,看到这个结果......
>>> t = Transaction.objects.all().first()
>>> t.type
0
>>> str(t.type)
'0'
【问题讨论】:
可以分享模型的完整代码吗?? 你能分享完整的堆栈跟踪吗? 你从哪里得到labels
字典的概念?看到这个问题:***.com/questions/54802616/… 枚举类型非常简单,你只需要一个包含两个值的元组,第二个是标签......
我的基本问题是,如果您将一个字段定义为一个整数,并且选择映射到这些元组,Django 是否提供了一种将该字段的实例转换为元组的字符串部分的巧妙方法。跨度>
【参考方案1】:
按照django official documentation for Django3.2 的一种更简单的方法
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, _('Buy')
SELL = 1, _('Sell')
(或)
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
另一种方法是利用Enum functional api,这在Django 3.2官方文档中也有提及
TransactionTypes = models.IntegerChoices('TransactionTypes', 'BUY SELL')
TransactionTypes.choices
#provides below output
>>>[(1, 'Buy'), (2, 'Sell')]
编辑:1
考虑到您只有少数交易类型(如买入/卖出和其他未来交易类型可能性,如交换或退货),我建议使用更适合您的场景的PositiveSmallIntegerField
。
此处PositiveSmallIntegerField
支持从 0 到 32767 的值,而 SmallIntegerField
支持从 -32768 到 32767 的值
语法:
models.PositiveSmallIntegerField(**Field Options)
例子:
class Transaction(models.Model):
class TransactionTypes(models.IntegerChoices):
BUY = 0, 'Buy'
SELL = 1, 'Sell'
start_transactionType= models.PositiveSmallIntegerField(choices=TransactionTypes.choices, default=TransactionTypes.BUY, help_text="Do you wish to Buy or Sell?", null=True, blank=True, primary_key=False, editable=True)
def __str__(self):
return '%s' % (self.start_transactionType)
__ str __ 是一种 Python 方法,它返回任何对象的字符串表示形式。这是 Django 用来将模型实例显示为纯字符串的方法。
字段选项
choices
:设置该字段的选项
default
:字段的默认值
help_text
:与表单小部件一起显示的额外“帮助”文本。即使您的字段未在表单上使用,它对文档也很有用
null
:如果设置为True
,Django 将空值作为NULL
存储在数据库中,默认为False
。
blank
:如果是True
,该字段允许为空,默认为False
primary_key
:如果是True
,这个字段是模型的主键,默认是False
editable
:如果False
,该字段将不会显示在管理员或任何其他模型窗体中。在模型验证期间它们也会被跳过。默认为 True
。
对于一个实时示例,您可以按照这个 5 部分教程系列, part 5: Fluent in Django: Get to know Django models better
编辑:2
枚举类中添加了许多自定义属性(.choices、.labels、.values 和 .names),以便更轻松地访问枚举中这些单独部分的列表。
根据 django 文档,可以使用 .label
属性或 .name
属性
TransactionTypes.BUY.label
>>> “Buy” #returns this output as string value
TransactionType.BUY.name
>>> “BUY” # returns this output
TransactionType.BUY.value
>>> 0 # returns this as output
EDIT 3基于更新的问题&cmets
编辑 3 中包含的简要信息
额外的实例方法引用自 django 3.2 doc 的示例 如何将额外的实例方法应用于您的用例 解决问题的变通功能Django 3.2 documentation on extra instance method 提及
对于每个设置了选项的字段,对象将有一个 get_FOO_display() 方法,其中 FOO 是字段的名称。此方法返回字段的“人类可读”值。 下面给出了文档中的示例示例
from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
>>>p = Person(name="Fred Flintstone", shirt_size="L") >>>p.save() >>>p.shirt_size ‘L’ #output >>> p.get_shirt_size_display() ‘Large’ #output
将额外的实例方法应用到您的用例
根据您更新的问题和 cmets,您提到 t
是 Transactions
对象的实例,type
是 PositiveSmallIntegerField
(TransactionTypes
选择的实例)
t.get_type_display() 代码理想情况下应该将输出Buy
生成为字符串
>>> type= models.PositiveSmallIntegersField(choices=TransactionTypes.choices, null=True, blank=True)
>>> t = Transaction.objects.all().first()
>>> t.type
0 #output
>>> t.get_type_display()
‘Buy’ #output
解决方法
一种解决方法是编写一个单独的函数来检查 int 枚举值并将标签作为字符串返回
def label_of_TransactionType:
if (t.type== TransactionType.BUY.value):
return TransactionTypes.BUY.label
else:
return TransactionTypes.SELL.label
【讨论】:
所以如果我有一个事务实例,我如何获取我的“类型”字段的字符串值,假设它被定义为“models.SmallIntegerField(null=False,choices=TransactionTypes.选择,)" 最好选择PositiveSmallIntegerField
而不是SmallIntegerField
,请根据您的cmets参考编辑
@Dave 你还需要使用 python 方法 __ str __ 来获取字符串值
我添加了我的问题,但 str(t.type) 没有给出字符串,只是数字的字符串化版本。根据您的建议,我确实将列类型更改为 PositiveSmallInteger。 (另外,“_('Buy')”会导致编译错误,所以我改用了“BUY = 0, 'Buy'”解决方案。
"t.get_type_display" 是赢家!正是我正在寻找的简单解决方案。谢谢!【参考方案2】:
我很欣赏一致的定义,但这个问题还有其他答案是使用枚举,我认为 Enum 类型是迄今为止最好的。它们可以同时显示一个项目的整数和字符串,同时使您的代码更具可读性。请参阅下面的代码 sn-ps:
app/enums.py
from enum import Enum
class ChoiceEnum(Enum):
def __str__(self):
return self.name
def __int__(self):
return self.value
@classmethod
def choices(cls):
choices = list()
for item in cls: # Loop thru defined enums
choices.append((item.value, item.name))
return tuple(choices)
class TransactionType(ChoiceEnum):
BUY = 0
SELL = 1
# Uh oh
TransactionType.BUY._name_ = 'Buy'
TransactionType.SELL._name_ = 'Sell'
app/models.py
from django.db import models
from myapp.enums import TransactionType
class Transaction(models.Model):
type_of_transaction = models.IntegerField(choices=TransactionType.choices(), default=int(TransactionType.BUY))
# ...
【讨论】:
按照您上面定义的方式,如果我有一个 Transaction 实例,我将如何访问“type_of_transaction”属性作为其字符串版本?【参考方案3】:一种方法是将选项定义为元组的元组,每个选项都是外部元组的值。 每个内部元组中的第一个元素是要在模型中设置的值,第二个元素是它的字符串表示形式。如下面的代码 sn-p:
class Transaction(models.Model):
BUY = 0
SELL = 1
TRANSACTION_TYPE_CHOICES = (
(BUY, 'Buy'),
(SELL, 'Sell'),
)
type_of_transaction = models.IntegerField(
choices=TRANSACTION_TYPE_CHOICES,
default=BUY,
)
【讨论】:
以上是关于在 Django 中,如何为 IntegerChoices 枚举定义字符串值?的主要内容,如果未能解决你的问题,请参考以下文章
django:DetailView 如何为两个模型工作或基于类的视图如何为两个模型工作?