Django 2.2使用原生Enum作为模型choices时属性相等判断异常问题
问题本质说明
该现象不是操作错误,是Django原生字段的设计逻辑导致的:Django ORM从数据库读取字符串类型字段值时,默认只会返回字符串,不会主动做Enum类型转换。你将普通Python Enum作为choices传入字段时,Django仅在写操作、查询过滤阶段会自动调用Enum的value属性转成对应字符串存库、拼接查询条件,但读场景没有做反向转换逻辑,所以出现了查询、判断逻辑不一致的问题。
版本相关说明
- 你使用的Django 2.2确实没有原生普通Enum的自动转换支持,即便是最新的Django 4.x/5.x版本,如果你只用普通Python Enum作为CharField的choices,也不会自动实现读时的类型转换。官方原生的自动类型转换仅针对Django自带的
models.Choices子类实现,所以该问题不是版本过旧直接导致的,仅升级版本不调整实现的话,问题会持续存在。
可行规避方案(兼容无法替换为Django Choices的要求)
按落地成本、稳定性排序推荐以下方案:
方案1:自定义Enum专属字段(最推荐,侵入性最低)
自定义继承自CharField的字段类型,重写读写阶段的转换逻辑,一劳永逸解决类型不一致问题,示例代码如下:
from enum import Enum from django.db import models class EnumCharField(models.CharField): def __init__(self, enum_class, *args, **kwargs): self.enum_class = enum_class # 自动注入choices配置,无需业务代码重复传 kwargs['choices'] = [(tag.value, tag.name) for tag in enum_class] super().__init__(*args, **kwargs) def from_db_value(self, value, expression, connection): # 从数据库读值时自动转成Enum实例 if value is None: return value return self.enum_class(value) def to_python(self, value): # 手动赋值、表单校验时自动转成Enum实例 if isinstance(value, self.enum_class): return value if value is None: return value return self.enum_class(value) def get_prep_value(self, value): # 存库时自动转成Enum对应的value字符串 if isinstance(value, self.enum_class): return value.value return super().get_prep_value(value)
替换原有模型的字段定义即可:
awesomeness = EnumCharField( enum_class=AwesomeNess, max_length=255, default=AwesomeNess.slight )
改造完成后,无论是从数据库读取的属性值还是手动赋值的属性值都是Enum类型,相等判断、查询过滤逻辑完全统一,无需修改其他业务代码。
方案2:统一判断规则(无需改模型,适合小项目)
如果不想调整模型定义,可约定所有相等判断统一调用Enum的value属性:
# 统一写法即可得到预期结果 choice_obj.awesomeness == AwesomeNess.slight.value
该方案无需调整现有模型结构,缺点是需要所有开发人员遵守编码规范,漏写容易出bug。
方案3:新增property封装转换逻辑
在原有模型上新增封装属性,自动把原始字符串值转成Enum:
class Choice(models.Model): # 原有字段定义不变 awesomeness = models.CharField( max_length=255, choices=[(tag.value, tag.name) for tag in AwesomeNess], default=AwesomeNess.slight ) @property def awesomeness_enum(self): return AwesomeNess(self.awesomeness)
后续业务逻辑处理、相等判断统一用awesomeness_enum属性,原始awesomeness字段仅在ORM查询时使用,也可实现逻辑统一。
内容的提问来源于stack exchange,提问作者Trondh
相关产品推荐
相关产品推荐

