在 Django 中,如何为 IntegerChoices 枚举定义一个字符串值?

In Django, how do I define a string value for IntegerChoices enum?

我正在使用 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'

一种方法是将选择定义为元组中的元组,每个选项都是外部元组的值。 每个内部元组中的第一个元素是要在模型中设置的值,第二个元素是它的字符串 representation.like 下面的代码片段:

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,
    )

我很欣赏一致的定义,但是这个问题还有其他答案是使用枚举,我认为 Enum 类型是迄今为止最好的。它们可以同时显示项目的整数和字符串,同时让您的代码更多 readable.see 下面的代码片段:

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))
# ...

根据 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

考虑到您只有少数交易类型(如 Buy/Sell 和其他未来交易类型的可能性,如 Exchange 或 Return),我建议使用 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 __ is a Python method that returns a string representation of any object. This is what Django uses to display model instances as a plain string.

字段选项

  • choices : 设置该字段的选项
  • default:字段的默认值
  • help_text:与表单小部件一起显示的额外“帮助”文本。即使您的字段未在表单上使用,它对文档也很有用
  • null:如果设置为True Django 在数据库中将空值存储为NULL,默认为False.
  • blank:如果True,该字段允许为空,默认为False
  • primary_key:如果True,该字段是模型的主键,默认为False
  • editable:如果False,该字段将不会显示在admin或任何其他ModelForm中。在模型验证期间也会跳过它们。默认为 True.

对于一个实例,您可以按照这个由 5 部分组成的教程系列进行操作, part 5: Fluent in Django: Get to know Django models better

编辑:2

A number of custom properties are added to the enumeration classes – .choices, .labels, .values, and .names – to make it easier to access lists of those separate parts of the enumeration.

根据 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 

编辑 3 基于更新的问题和评论

编辑 3 中涵盖的简要信息

  • extra instance 方法 example quoted from django 3.2 doc
  • 如何将额外实例方法应用到您的用例
  • 解决问题的变通函数

Django 3.2 documentation on extra instance method 提及

For every field that has choices set, the object will have a get_FOO_display() method, where FOO is the name of the field. This method returns the “human-readable” value of the field. Sample example from documentation is given below

 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

额外实例方法应用到您的用例

根据您更新的问题和评论,您提到 tTransactions 对象的实例,typePositiveSmallIntegerField([=45 的实例=]选择)

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 枚举值和 return 标签作为字符串

  def label_of_TransactionType:
       if (t.type== TransactionType.BUY.value):
               return TransactionTypes.BUY.label
         else:
               return TransactionTypes.SELL.label