在 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 存储在数据库中,默认为Falseblank:如果是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,您提到 tTransactions 对象的实例,typePositiveSmallIntegerFieldTransactionTypes 选择的实例)

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 如何为两个模型工作或基于类的视图如何为两个模型工作?

如何为 Django 启用 WSGIPassAuthorization?

如何为 django 可重用应用程序启动测试?

Django如何为多个表单创建一个帖子请求。

我如何为 django 1.8 编写初始数据

如何为 Django 模型定义默认数据?