You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:12:26