【发布时间】:2021-01-16 15:52:08
【问题描述】:
我想将Django 3.0 TextChoices 用作models.CharField choices option 并在我的代码中的其他地方用作Enum。
这是我的代码的简化版本:
from django.db import models
class ValueTypeOriginal(models.TextChoices):
# I know Django will add the label for me, I am just being explicit
BOOLEAN = 'boolean', 'Boolean'
class Template(models.Model):
value_type = models.CharField(choices=ValueTypeOriginal.choices)
我想为枚举成员添加一个附加属性,以便调用
>>> ValueType.BOOLEAN.native_type
bool
有效。
bool 这里不是字符串,而是built-in python function。
This blog post 描述了通过覆盖__new__ 来使用Enum 做类似的事情。
class Direction(Enum):
left = 37, (-1, 0)
up = 38, (0, -1)
right = 39, (1, 0)
down = 40, (0, 1)
def __new__(cls, keycode, vector):
obj = object.__new__(cls)
obj._value_ = keycode
obj.vector = vector
return obj
基于我尝试过的:
class ValueTypeModified(models.TextChoices):
BOOLEAN = ('boolean', bool), 'Boolean'
def __new__(cls, value):
obj = str.__new__(cls, value)
obj._value_, obj.native_type = value
return obj
这几乎行得通。我可以访问独特的TextChoices 属性,如.choices,并且我有属性.native_type,但字符串比较不能正常工作。
>>> ValueTypeOriginal.BOOLEAN == 'boolean'
True
>>> ValueTypeModified.BOOLEAN == 'boolean'
False
我想我误解了__new__ 方法,但我不知道我应该做些什么不同。
更新
为了回应 Ethan Furman 的回答,我尝试了
class ValueType(TextChoices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value, type):
obj = str.__new__(value)
obj._value_ = value
obj.native_type = type
return obj
但得到
TypeError: __new__() missing 1 required positional argument: 'type'
所以我又去了
class ValueType(TextChoices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value):
obj = str.__new__(value)
obj._value_ = value[0]
obj.native_type = value[1]
return obj
但我明白了:
TypeError: str.__new__(X): X is not a type object (tuple)
那么
class ValueType(TextChoices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value):
obj = str.__new__(cls)
obj._value_ = value[0]
obj.native_type = value[1]
return obj
让我回到我开始直接字符串比较失败的地方
>>> ValueTypeOriginal.BOOLEAN == 'boolean'
True
>>> ValueType.BOOLEAN == 'boolean'
False
然而,
>>> ValueType.BOOLEAN.value == 'boolean'
True
所以正确的值似乎到达了那里,但枚举成员本身的评估不像ValueType(str, Enum),而是在比较时像ValueType(Enum)。
更新 #2
我现在已经试过了:
class ValueType(TextChoices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value):
obj = str.__new__(cls, value)
obj._value_ = value[0]
obj.native_type = value[1]
return obj
class ValueType(str, Choices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value):
obj = str.__new__(cls)
obj._value_ = value[0]
obj.native_type = value[1]
return obj
为了安全起见
class ValueType(TextChoices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value):
obj = super().__new__(cls, value)
obj._value_ = value[0]
obj.native_type = value[1]
return obj
但没有一个按预期给我直接的字符串比较。
更新 #3 我终于明白 Ethan Furman 告诉我要做什么了。
解决方案:
class ValueType(TextChoices):
BOOLEAN = (('boolean', bool), 'Boolean')
def __new__(cls, value):
obj = str.__new__(cls, value[0])
obj._value_ = value[0]
obj.native_type = value[1]
return obj
【问题讨论】:
标签: python django django-models enums metaclass