所以我正在使用一个大型Django代码库,该代码库始终使用python Enums,例如:
from enum import Enum
class Status(Enum):
active = 'active'
# ... later
assert some_django_model_instance.status == Status.active.value # so far so good
...但是,“。value”部分当然会被遗忘并保留一直”>。到目前为止,要完全放弃Enums是很困难的,尽管它们存在的问题多于有用。有没有一种方法可以自动检查像这样的行:
<<assert some_django_model_instance.status == Status.active # someone forgot ".value" here!
,例如,mypy或pylint,或者可能向基本枚举添加一些代码/断言?问题是,
Status.active
并没有真正调用任何代码,它只是返回一个类,当然,该类绝不等于some_django_model_instance.status
,它是一个字符串。
enum.EnumMeta
现在您不必调用
from enum import EnumMeta, Enum as _Enum class Enum(_Enum, metaclass=EnumMeta): def __eq__(self, arg): if isinstance(arg, self.__class__): return arg is self return self.value == arg
进行比较:enum.value
这从某种意义上解决了这个问题,即其他人不会忘记调用class Method(Enum): GET = 'GET' POST = 'POST' >>> get = 'GET' >>> Method.GET == get True >>> get == Method.GET True >>> Method.POST == Method.GET False
进行比较,但是会产生更大的问题,因为现在插入模型中时,人们更可能会忘记调用.value
。要解决此问题,建议您也将
子类化以创建您自己的枚举字段:
.value
models.CharField
现在您也可以在不调用models.CharField
的情况下插入模型:
class EnumField(models.CharField):
def __init__(self, enum, **kwargs):
self.enum = enum
def from_db_value(self, value, expression, connection):
if value is not None:
return self.enum(value)
return None
def to_python(self, value):
if isinstance(value, self.enum):
return value.value
return None
def get_prep_value(self, value):
if isinstance(value, self.enum):
value = value.value
return super().get_prep_value(value)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
args.append(self.enum)
return name, path, args, kwargs
总是为假的相等比较,而不仅仅是涉及枚举的比较。 但是,对于任何一个操作数都定义了自定义您可以使用.value
命令行标志/配置标志选项使mypy检测这些类型的有问题的比较。启用此标志后,执行class MyModel(models.Model): method = EnumField(enum=Method) >>> MyModel.objects.create(method=Method.GET)
会产生如下错误:注意:此标志将检查all
--strict-equality
方法的相等比较,它将禁用,因为自定义方法实际上可以做任何事情。
.value
命令行标志/配置标志选项使mypy检测这些类型的有问题的比较。启用此标志后,执行class MyModel(models.Model):
method = EnumField(enum=Method)
>>> MyModel.objects.create(method=Method.GET)
会产生如下错误: