Django模型实现年份+递增序号格式自定义ID的方案咨询
方案建议
优先选择:保留默认ID,新增自定义业务ID字段
该方案是生产环境下的最优选择,核心优势如下:
- 解耦业务规则与数据库主键:你需要的【年份+递增序号】属于业务层面的编号规则,后续如果规则调整(比如需要增加月份、标识前缀等),不需要修改主键关联逻辑,维护成本极低
- 性能更优:Django默认的自增整数主键作为数据库聚簇索引,查询、外键关联的性能远高于字符串类型的自定义主键,存储开销也更小
- 兼容性更好:绝大多数Django第三方生态包默认适配自增整数主键,替换主键会增加不必要的适配成本
不推荐:直接替换默认主键为自定义ID
该方案仅在业务强制要求该字段作为唯一主键时考虑,存在明显弊端:
- 字符串主键会大幅降低索引、关联查询性能
- 已存在存量数据的情况下做主键迁移风险极高
- 业务规则调整时需要全表更新主键,关联的外键数据也需要同步修改,运维成本极高
具体实现代码示例
from django.db import models from django.db.utils import IntegrityError class YourBusinessModel(models.Model): # 保留Django默认自增ID主键 create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # 自定义业务ID,设置不可编辑、唯一约束 business_id = models.CharField(max_length=7, unique=True, editable=False, verbose_name="业务编号") # 其他业务字段... def save(self, *args, **kwargs): # 仅新增实例时生成业务ID,更新时不修改 if not self.pk: # 增加重试机制避免并发写入导致的编号重复 max_retries = 3 for _ in range(max_retries): year = self.create_time.year # 统计当年已生成的记录数量 year_count = YourBusinessModel.objects.filter(create_time__year=year).count() current_seq = year_count + 1 # 按需求拼接年份+3位补零序号,示例2021001 self.business_id = f"{year}{current_seq:03d}" try: super().save(*args, **kwargs) break except IntegrityError: # 触发唯一约束冲突说明并发抢号,重试生成 continue else: raise RuntimeError("业务编号生成失败,重试次数已达上限") else: super().save(*args, **kwargs)
注意事项
- 若单年业务量可能超过999,可调整序号补位长度,比如改为
current_seq:04d,同步将business_id的max_length调整为8即可 - 若并发写入量极高,可以单独维护一张序号生成表,按年份存储当前已分配的最大序号,生成编号时直接查表更新,减少统计查询的开销
内容的提问来源于stack exchange,提问作者user14788009
相关产品推荐
相关产品推荐

