Django自定义User模型后外键约束报错:字段类型不匹配
问题场景
自定义User模型代码:
from django.contrib.auth.models import AbstractUser class User(AbstractUser): class Meta: app_label = "myapp"
关联该User模型的TestTable模型代码:
class TestTable(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) value = models.FloatField()
执行makemigrations和migrate时出现报错:
(3780, "Referencing column 'user_id' and referenced column 'id' in foreign key constraint")
查看数据库表结构:myapp_user表的主键类型:
mysql> describe myapp_user; +--------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+--------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | ... | ... | ... | ... | ... | ... | +--------------+--------------+------+-----+---------+----------------+
myapp_testtable表的外键类型:
mysql> describe myapp_testtable; +---------+--------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------+------+-----+---------+----------------+ | user_id | bigint | NO | | NULL | | | ... | ... | ... | ... | ... | ... | +---------+--------+------+-----+---------+----------------+
问题原因
Django 3.2版本对默认主键类型做了变更:3.2之前的版本中,AbstractUser的主键默认使用AutoField(对应MySQL的int类型);而3.2及以后的版本,默认主键类型改为BigAutoField(对应MySQL的bigint类型)。
你的myapp_user表是基于旧版本Django创建的,主键为int;但当前使用的是新版本Django,新创建的TestTable模型的外键字段会自动采用新版本的默认主键类型bigint,两者类型不匹配,导致MySQL无法创建外键约束。
解决方法
方法一:修改User模型,统一主键类型为bigint
修改User模型,显式指定主键为BigAutoField:
from django.contrib.auth.models import AbstractUser from django.db.models import BigAutoField class User(AbstractUser): id = BigAutoField(primary_key=True) class Meta: app_label = "myapp"
执行以下命令完成迁移:
python manage.py makemigrations python manage.py migrate
此操作会将myapp_user表的id字段类型改为bigint,与TestTable的user_id类型匹配,外键约束可正常创建。
方法二:修改TestTable外键字段类型,匹配User表的int
如果不想改动已有的myapp_user表,可修改TestTable的外键字段,显式指定数据库类型为int:
from django.db import models from django.conf import settings class TestTable(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, db_type='int' ) value = models.FloatField()
重新生成迁移并执行:
python manage.py makemigrations python manage.py migrate
此操作会将TestTable的user_id字段改为int类型,与User表的主键类型一致。
内容的提问来源于stack exchange,提问作者Sabrebar

