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

如何让Django识别通过RunSQL迁移添加的SERIAL非主键列?

解决Django中添加PostgreSQL非主键SERIAL列的迁移同步问题

问题背景

需要给现有Django模型添加一个非主键的PostgreSQL SERIAL类型列,但Django不支持将AutoField设为非主键。直接用RunSQL添加SERIAL列后,Django迁移系统不会记录该字段的存在,再次执行manage.py makemigrations时会重复生成添加该字段的操作。

模型定义如下:

class Referral(utils.UUIDPrimaryKeyBase, utils.TimeStampedModel):
    data = models.JSONField(encoder=DjangoJSONEncoder)
    supplier = models.ForeignKey(Supplier, blank=True, null=True, on_delete=models.PROTECT, related_name="referrals")
    session_id = models.UUIDField(editable=False, blank=True, null=True, unique=True)
    referral_id = models.IntegerField()

    def __str__(self):
        return f"<referral id={self.id} supplier={self.supplier}>"

优雅解决方案:用RunSQL配合state_operations

无需先添加再删除字段,直接通过RunSQL的state_operations参数同步Django迁移状态,既在数据库中创建SERIAL列,又避免后续重复生成迁移操作。

修改后的迁移文件如下:

class Migration(migrations.Migration):

    dependencies = [
        # 保留原依赖配置
    ]

    operations = [
        migrations.RunSQL(
            sql="ALTER TABLE portal_referral ADD referral_id SERIAL;",
            reverse_sql="ALTER TABLE portal_referral DROP referral_id;",
            # 告知Django迁移系统该字段已完成添加,匹配模型定义
            state_operations=[
                migrations.AddField(
                    model_name='referral',
                    name='referral_id',
                    field=models.IntegerField(),
                )
            ]
        )
    ]

原理说明

  • state_operations参数用于更新Django的迁移状态(即django_migrations表记录的模型快照),让Django认为该字段已通过标准迁移流程添加完成。
  • 实际数据库操作通过sql参数执行PostgreSQL专属的SERIAL创建语句,保证字段具备自动生成序列、自增的特性。
  • 反向迁移时,reverse_sql负责删除字段,state_operations会自动处理状态回滚逻辑。

临时方案的弊端

你之前的方法先添加默认值为0的IntegerField,再删除重建为SERIAL,会产生三次数据库操作(添加、删除、重建),而上述方案仅需一次ALTER TABLE操作,同时同步迁移状态,效率更高且逻辑更清晰。

内容的提问来源于stack exchange,提问作者Samuel Young

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:52:39