【问题标题】:Django IntegrityError changing a ForeignKeyDjango IntegrityError 更改外键
【发布时间】:2018-03-30 20:32:53
【问题描述】:

我有一个模型 LucyGuide,它通过 OneToOneField 扩展了 Django 的 User 模型:

class LucyGuide(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

还有一个Company 模型,它有一个名为lucy_guide 的字段,它是User 模型的ForeignKey

class Company(models.Model):
    lucy_guide = models.ForeignKey(User)

我想把它改成

class Company(models.Model):
    lucy_guide = models.ForeignKey(LucyGuide)

但是,当我实施此更改并进行和运行迁移时,我遇到了IntegrityError

django.db.utils.IntegrityError: insert or update on table "lucy_web_company" violates foreign key constraint "lucy_web_company_lucy_guide_id_de643702_fk_lucy_web_"
DETAIL:  Key (lucy_guide_id)=(461) is not present in table "lucy_web_lucyguide".

这个问题类似于IntegrityError Insert or update on table "orders_order" violates foreign key constraint ";似乎我在将 LucyGuide 对象引用为外键之前创建了它们。

解决此问题的最佳方法是什么?我需要在 shell 中编写一系列命令来创建这些用户吗?

更新

从 shell 中环顾四周,似乎 Django 仍然希望 ForeignKey 的数字 ids 相同,即使模型已经更改(从 UserLucyGuide)。这是Users 中的ids,它们也是LucyGuides:

In [11]: lucy_guide_users = [user for user in User.objects.all() if hasattr(user, 'lucyguide')]

In [16]: [user.id for user in lucy_guide_users]
Out[16]: [12, 8, 461, 497, 500, 471, 475, 495]

请注意,这包含来自错误的461id。但是,LucyGuides 的 ids 只是

In [17]: [guide.id for guide in LucyGuide.objects.all()]
Out[17]: [1, 2, 3, 4, 5, 6, 7, 8]

似乎解决此问题的方法是更改​​LucyGuides 的主键,但从What is the best approach to change primary keys in an existing Django app? 看来,这是一个非常复杂的过程。有没有更简单的方法?

【问题讨论】:

  • 您不应尝试更改LucyGuide 表中对象的主键。相反,您想更改Company 表中的lucy_guide_id 值,以便它们引用LucyGuide 行。不幸的是,这是一个相当复杂的过程。 question you linked 是关于更改主键的字段类型(例如,从 CharFieldAutoField),这是一个不同的问题。

标签: python django database postgresql


【解决方案1】:

您不能简单地更改外键的目标。对于每个现有公司,您需要将lucy_guide_id 从相关用户的 id 更改为相关的 lucy_guide 的 id。 Django 无法为你做到这一点。

您可以分几个步骤进行迁移。首先,添加一个新的外键并创建一个迁移

class Company(models.Model):
    lucy_guide = models.ForeignKey(User)
    new_lucy_guide = models.ForeignKey(LucyGuide, blank=True, null=True)

接下来,创建一个data migration 来填充new_lucy_guide 字段。

然后您可以删除lucy_guide 字段并创建迁移。

class Company(models.Model):
    new_lucy_guide = models.ForeignKey(LucyGuide, blank=True, null=True)

最后,您可以重命名字段并创建迁移:

class Company(models.Model):
    lucy_guide = models.ForeignKey(LucyGuide, blank=True, null=True)

【讨论】:

  • 准确地说:您不能同时执行最后两个步骤。它会将新字段视为RemoveField,并将旧字段上的名称更改为AlterField
  • @PierreMonico 感谢您的确认。我删除了建议您可能将最后两个步骤合并到一个迁移中的段落。
  • 唯一的办法就是手动编辑迁移文件。
猜你喜欢
  • 1970-01-01
  • 2018-05-17
  • 2012-07-23
  • 2018-03-18
  • 2020-06-10
  • 1970-01-01
  • 2017-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多