Django中状态存储用独立模型还是choices字段相关问题咨询
问题解答
1. 独立模型存储订单状态的弊端
- 不必要的性能开销:每次读取订单状态都需要关联查询
OrderStatus表,批量查询订单时极易出现N+1查询问题,即便使用select_related做关联优化,也属于多余的性能损耗 - 维护成本更高:新增/修改状态时需要操作数据库,要么编写数据迁移脚本,要么在后台手动录入数据,很容易出现开发、测试、生产多环境状态数据不一致的问题
- 缺少静态校验:业务代码中判断状态时只能写字符串匹配,比如
if order.status.name == "UNPAID",写错字符串不会在开发阶段触发报错,容易留下隐蔽bug - 数据安全风险:你当前设置的外键删除规则是
CASCADE,一旦误删某条OrderStatus记录,所有关联该状态的订单会被级联删除,存在严重的安全隐患
2. 重构为choices字段后新增状态的注意事项
通常情况下新增状态只需要修改ORDER_STATUS_CHOICES列表即可,但需要注意以下场景的额外操作:
- 首先你当前示例代码中的
status字段max_length设置为2属于明显错误,UNPAID、PAID的字符长度都超过2,需要先将max_length修改为匹配状态值的长度,否则数据存储会直接报错 - 如果新增的状态需要设为新的默认值,需要同步修改字段的
default参数 - 如果需要给存量的历史订单批量更新为新状态,需要编写对应的数据迁移脚本
- 若项目中使用了序列化器(比如Django REST Framework的Serializer)、表单类对
status字段做了独立的choices校验,需要同步更新对应位置的配置 - 业务代码中如果存在写死的状态判断逻辑,也需要同步新增对应状态的分支处理
优化建议:Django 3.0及以上版本更推荐使用
TextChoices枚举类实现choices,代码可读性和可维护性更高,示例如下:class Order(models.Model): class Status(models.TextChoices): UNPAID = 'UNPAID', '未支付' PAID = 'PAID', '已支付' CANCELLED = 'CANCELLED', '已取消' status = models.CharField( max_length=10, choices=Status.choices, default=Status.UNPAID, )使用枚举类后判断状态可以直接写
if order.status == Order.Status.UNPAID,写错属性名会直接在IDE阶段提示错误,进一步降低bug概率。
内容的提问来源于stack exchange,提问作者Shalahuddin Al-Ayyubbi
相关产品推荐
相关产品推荐

