在Django 1.11中为UUID主键模型添加自增可读标识
解决方案:为UUID主键的Django模型添加自增用户可读标识
针对你的Django 1.11 + PostgreSQL场景,这里有一套兼顾并发安全、历史数据兼容的完整方案,分步骤给你拆解:
1. 新增用户可读标识字段
首先在模型里添加用于存储自增编号的字段,初期允许空值是为了先填充历史数据:
import uuid from django.db import models from django.db import transaction class model_name(models.Model): # 原有UUID主键字段 companyId = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # 新增的用户可读自增编号字段 public_id = models.PositiveIntegerField(unique=True, null=True, blank=True, verbose_name="用户可读编号") # 其他原有字段...
2. 创建PostgreSQL序列(解决并发冲突的核心)
直接用PostgreSQL原生的**序列(Sequence)**来生成自增编号,是最可靠的并发安全方案。执行python manage.py makemigrations生成添加字段的迁移后,手动修改迁移文件,加入创建序列的SQL:
from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ # 替换为你的迁移依赖,比如('your_app', '00xx_previous_migration'), ] operations = [ migrations.AddField( model_name='model_name', name='public_id', field=models.PositiveIntegerField(blank=True, null=True, unique=True, verbose_name='用户可读编号'), ), # 创建从100000开始的序列,关联到新增字段 migrations.RunSQL( "CREATE SEQUENCE model_name_public_id_seq START 100000 OWNED BY your_app_model_name.public_id;", # 回滚操作 "DROP SEQUENCE model_name_public_id_seq;" ), ]
注意替换your_app为你的应用名称,your_app_model_name是Django自动生成的数据库表名(格式为应用名_模型名小写)。
3. 批量填充历史记录的编号
针对已有的20000条数据,用PostgreSQL窗口函数高效批量生成连续编号,避免Python循环的性能损耗:
在Django Shell或数据库客户端执行以下SQL:
UPDATE your_app_model_name SET public_id = 100000 + row_number() OVER (ORDER BY companyId) - 1;
这里ORDER BY companyId是按UUID排序,你也可以替换为created_at(如果有创建时间字段)来保证编号按记录创建顺序生成,只要保证唯一即可。
填充完成后,修改模型字段去掉null=True, blank=True,生成新的迁移并执行,确保字段变为不可空:
public_id = models.PositiveIntegerField(unique=True, verbose_name="用户可读编号")
4. 重写save()方法实现自动赋值
在模型的save()方法中,为新建记录自动从序列获取编号,确保并发场景下不会冲突:
def save(self, *args, **kwargs): # 仅在新建记录且public_id未设置时触发自动赋值 if not self.public_id and not self.pk: with transaction.atomic(): from django.db import connection with connection.cursor() as cursor: # 原子性获取序列的下一个值 cursor.execute("SELECT nextval('model_name_public_id_seq');") self.public_id = cursor.fetchone()[0] super().save(*args, **kwargs)
为什么不用max(public_id)+1?
直接取最大值加1的方式在并发场景下会有冲突:两个请求同时读取到相同的最大值,会生成重复编号。而PostgreSQL的nextval()是数据库层面的原子操作,每次调用都会返回唯一的递增数值,彻底避免并发冲突。
5. 验证方案有效性
- 并发测试:可以写一个简单脚本同时创建多条记录,检查
public_id是否唯一且递增(允许少量间隙,比如事务回滚导致的序列跳过,不影响用户使用); - 历史数据检查:执行
SELECT COUNT(DISTINCT public_id) FROM your_app_model_name;,确认计数与总记录数一致,且编号从100000开始连续。
内容的提问来源于stack exchange,提问作者kmcodes
相关产品推荐
相关产品推荐

