Django模型迁移数据库时出现外键不匹配错误求助
Django迁移报错:foreign key mismatch - "user_auth_customer" referencing "user_auth_profile"
错误信息
return Database.Cursor.execute(self, query) django.db.utils.OperationalError: foreign key mismatch - "user_auth_customer" referencing "user_auth_profile"
问题描述
迁移模型到数据库时出现上述外键不匹配错误,已检查ForeignKey但未发现明显问题,相关模型代码如下:
Customer模型
class Customer(AbstractUser): USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] objects = UserManager() id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False) profile = models.OneToOneField("Profile", related_name="user_profile", on_delete=models.CASCADE, null=True) first_name = models.CharField(max_length=50, null=True, blank=True) last_name = models.CharField(max_length=50, null=True, blank=True) username = models.CharField(max_length=30, null=True, blank=True) phone = models.CharField(max_length=10, default='', null=True, blank=True) email = models.EmailField(validators=[validators.EmailValidator()], unique=True) password = models.CharField(max_length=100, null=True, blank=True) date_created = models.DateTimeField(auto_now_add=True) @staticmethod def get_customer_by_email(email): try: return Customer.objects.get(email=email) except: return False def isExists(self): if Customer.objects.filter(email=self.email): return True return False class Meta: verbose_name = 'Customer' verbose_name_plural = 'Customers'
Profile模型
class Profile(models.Model): first_name = models.CharField(max_length=50, null=True, blank=True) last_name = models.CharField(max_length=50, null=True, blank=True) phone = models.CharField(max_length=10, default='', null=True, blank=True) email = models.EmailField(primary_key=True, unique=True, validators=[validators.EmailValidator()]) password = models.CharField(max_length=100, null=True, blank=True) # Add a photo field owner = models.OneToOneField(Customer, related_name='profile_owner', on_delete=models.SET_NULL, null=True) username = models.CharField(max_length=30, null=True, blank=True, validators=[UnicodeUsernameValidator()]) date_created = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = 'Profile' verbose_name_plural = 'Profiles'
解决方案
1. 移除AbstractUser重复定义的字段
Customer继承自AbstractUser,该基类已包含id、username、email、password、first_name、last_name等字段,重复定义会导致数据库表结构冲突,是引发错误的核心原因之一。修改后的Customer模型:
class Customer(AbstractUser): USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] objects = UserManager() # 仅覆盖主键为UUID,其余字段使用AbstractUser自带的 id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False) phone = models.CharField(max_length=10, default='', null=True, blank=True) date_created = models.DateTimeField(auto_now_add=True) @staticmethod def get_customer_by_email(email): try: return Customer.objects.get(email=email) except: return False def isExists(self): return Customer.objects.filter(email=self.email).exists() class Meta: verbose_name = 'Customer' verbose_name_plural = 'Customers'
2. 解决循环OneToOne引用问题
当前两个模型互相定义OneToOneField,导致迁移时循环依赖,且完全没必要——Django的OneToOneField本身支持双向访问。只需保留其中一个模型的关联字段即可,推荐在Profile中保留关联:
修改Profile模型,同时不推荐用业务字段作为主键:
class Profile(models.Model): first_name = models.CharField(max_length=50, null=True, blank=True) last_name = models.CharField(max_length=50, null=True, blank=True) phone = models.CharField(max_length=10, default='', null=True, blank=True) # 改用UUID作为主键,避免用可变的email id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False) password = models.CharField(max_length=100, null=True, blank=True) # 关联Customer,related_name用于从Customer反向访问Profile owner = models.OneToOneField(Customer, related_name='profile', on_delete=models.SET_NULL, null=True) username = models.CharField(max_length=30, null=True, blank=True, validators=[UnicodeUsernameValidator()]) date_created = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = 'Profile' verbose_name_plural = 'Profiles'
3. 清理旧迁移文件并重新生成
执行以下步骤重置迁移:
- 删除app下的
migrations文件夹中除__init__.py外的所有文件 - 清空数据库(或手动删除对应的表)
- 重新生成迁移文件:
python manage.py makemigrations - 执行迁移:
python manage.py migrate
关键说明
- 避免用业务字段(如email)作为主键:主键应具备不可变性,email可能需要修改,会导致关联数据异常。
- 继承AbstractUser时不要重复定义基类已有字段:容易引发字段类型、约束冲突,导致迁移失败。
- 双向关联无需重复定义:通过
related_name即可实现反向访问,减少循环依赖风险。
内容的提问来源于stack exchange,提问作者Vlad
相关产品推荐
相关产品推荐

