Django3.1如何实现按branch外键分组自增的唯一自定义字段
实现方案
你可以通过重写模型的save方法实现该需求,这是最直接且易维护的方案,同时配套数据库层面的联合唯一约束保证数据一致性,具体实现如下:
第一步:修改模型定义
新增自定义自增字段,重写save方法实现按分支计数逻辑,同时加事务锁避免并发重复:
from django.db import models, transaction from django.db.models import Max # 注意替换外键关联的对应模型名 class sales_order(models.Model): customer = models.ForeignKey(Customer, on_delete=models.CASCADE) item = models.ForeignKey(Item, on_delete=models.CASCADE) branch = models.ForeignKey(Branch, on_delete=models.CASCADE) # 自定义按分支自增字段,editable=False表示不需要后台/前端手动传入 branch_order_seq = models.IntegerField(blank=True, editable=False) # 其他原有字段... class Meta: # 数据库层面加联合唯一约束,比序列化器层的校验更可靠,避免并发脏数据 constraints = [ models.UniqueConstraint(fields=['branch', 'branch_order_seq'], name='unique_branch_order_seq') ] def save(self, *args, **kwargs): # 仅新建订单时生成序号,编辑已有订单不修改序号 if not self.pk: # 开启事务加行锁,防止并发请求时出现序号重复 with transaction.atomic(): # 查询当前分支下的最大序号,无数据时默认从1开始 current_max = sales_order.objects.select_for_update().filter(branch=self.branch).aggregate( max_seq=Max("branch_order_seq") )['max_seq'] or 0 self.branch_order_seq = current_max + 1 super().save(*args, **kwargs)
第二步:调整序列化器配置
将自定义序号字段设为只读,不需要用户传入:
class order_serializer(serializers.ModelSerializer): # 设为只读字段,自动从模型层获取值 branch_order_seq = serializers.IntegerField(read_only=True) class Meta: model = sales_order fields = ['id', 'customer', 'item', 'branch', 'branch_order_seq', ...] # 填入你需要暴露的所有字段 # 模型层已经加了唯一约束,这里的联合校验可以保留也可以移除 validators = [ UniqueTogetherValidator( queryset=sales_order.objects.all(), fields=['branch_order_seq', 'branch'] ) ]
注意事项
- 如果你的项目已有历史订单数据,上线该功能前需要先批量给历史数据按分支补全
branch_order_seq的值,避免后续新建订单时序号冲突 - 订单创建并发量极高的场景下,可以给
branch字段加索引,提升最大值查询的性能,也可以考虑用数据库触发器实现序号生成,进一步降低应用层压力 - 如果你不想重写save方法,也可以用Django的
pre_save信号实现相同逻辑,核心计算逻辑和上述代码一致
内容的提问来源于stack exchange,提问作者Ranu Vijay
相关产品推荐
相关产品推荐

