如何让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
相关产品推荐
相关产品推荐

