Django migrate报错:外键列与引用列数据类型不匹配
问题根因
报错本质是Django 3.2默认主键类型变更引发的外键类型不匹配:
- Django 3.2版本起,全局默认
DEFAULT_AUTO_FIELD配置为BigAutoField,对应SQL Server数据库的bigint类型 - 存量模型
ExistingLegacyModel由inspectdb生成,主键key显式定义为AutoField,对应SQL Server的int类型 - 定义外键时如果不显式指定字段类型,Django会跟随全局配置生成
bigint类型的外键列,和存量表的int类型主键类型不一致,SQL Server不允许在不同类型的列上建立外键约束,直接抛出类型匹配错误。
可行解决方案
方案1(推荐,无副作用)
直接在外键定义中显式指定列类型,和关联主键的int类型保持一致,不需要修改全局配置、不需要改动存量模型:
修改TestModel的外键字段代码:
class TestModel(models.Model): period = models.ForeignKey( ExistingLegacyModel, on_delete=models.CASCADE, db_column="OutlookKey", # 显式指定外键列类型为int,和存量表主键匹配 db_type=models.IntegerField(), )
修改后重新执行python manage.py makemigrations生成新的迁移文件,再执行migrate即可正常完成建表和约束创建,此时外键列会生成为标准int类型,和存量表主键完全兼容。
方案2(适配存量库为主的项目)
如果项目大部分表都是历史存量的int类型主键,可以直接在项目settings.py中修改全局默认自动字段配置,回退到Django 3.2之前的默认值:
# settings.py DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
配置生效后,所有未显式指定主键类型的新模型、未显式指定类型的外键字段都会默认使用int类型,自动和存量表匹配。如果项目后续有数据量超过int范围的大表需要bigint主键,不建议使用这个方案。
方案3(直接调整迁移文件)
如果不想修改模型层代码,可以直接编辑已生成的迁移文件,给外键字段显式指定类型,找到迁移文件里CreateModel操作的period字段定义,修改为:
('period', models.ForeignKey( db_column='OutlookKey', on_delete=django.db.models.deletion.CASCADE, to='app.ExistingLegacyModel', db_type=models.IntegerField() )),
修改后直接执行migrate即可正常运行。注意后续如果重新生成迁移文件,这个手动修改可能被覆盖,稳定性不如方案1。
验证步骤
正式执行迁移前,可以先运行python manage.py sqlmigrate <你的app名> <迁移文件编号>查看生成的SQL语句,确认外键列OutlookKey的类型是int而非bigint,再正式执行迁移即可避免报错。
内容的提问来源于stack exchange,提问作者Aashay Amballi
相关产品推荐
相关产品推荐

