【问题标题】:Deleting auto created model which is under PROTECTED ForeignKey删除受保护的 ForeignKey 下的自动创建模型
【发布时间】:2015-08-11 18:57:18
【问题描述】:
class Basket:
    name = models.CharField(max_length=50, blank=True, null=True)

class Apple:
    name = models.CharField(max_length=50, blank=True, null=True)
    basket = models.ForeignKey(Basket, on_delete=models.PROTECT)

...

myapple = new Apple(name="my")
myapple.save()

...

auto_created_basket = myapple.basket
myapple.basket = existing_basket
auto_created_basket.delete()

我尝试将 auto_created_basket 换成另一个,但是当我尝试删除它时出现错误。

"Cannot delete some instances of model 'Basket' because they are referenced through a protected foreign key: 'Apple.basket'", [<Apple: My apple>])

【问题讨论】:

  • 这是因为苹果中的外键引用了篮子里的东西,所以如果你删除篮子,那么苹果中的那个变量就会混淆。
  • first: myapple = new Apple(name="my") 会抛出语法错误。删除new。第二:你应该指定苹果应该放在哪个篮子里。 Apple(name='my', basket=b) 其中b 是一个实际的购物篮实例。

标签: python django foreign-key-relationship


【解决方案1】:

在您的 Apple 模型中,basket 字段是一个外键

basket = models.ForeignKey(Basket, on_delete=models.PROTECT)

on_delete 属性值专门声明通过防止删除篮子来保护苹果。

正如official docs 所说的

当一个 ForeignKey 引用的对象被删除时,Django 通过 默认模拟 SQL 约束 ON DELETE CASCADE 的行为 并删除包含 ForeignKey 的对象。

PROTECT 参数通过引发 受保护的错误

因此,最简单的步骤应该是删除 on_delete 参数并使用默认行为

basket = models.ForeignKey(Basket)

但是,请查看ForeignKey model field 的所有可能参数,然后选择适合您的应用程序/场景要求的组合。

更新:

最近的 Django 版本需要on_delete。只是不要删除它并添加您想要的参数(如on_delete=models.CASCADE)。

【讨论】:

  • 只是一个简短的评论,因为这个答案在谷歌上的排名很高并且包含一个逻辑错误:on_delete 没有指定Apple 不应该在Basket 被删除时被删除:相反,它指定只要Apple 仍在其中,就不能删除Basket。您的官方文档引用也指出,只是上面的解释与之相矛盾:-) - 否则很好回答
【解决方案2】:

我不想回答我的问题,但我的例子过于简化了。答案非常好。

在实际产品中,涉及到post_save信号,例如负责auto_created_basket的创建。

问题是当我说myapple.basket = existing_basket Django 的层很好,但数据库仍然持有对旧关系的引用。我的解决方案是在我再次保存myapple 之后移动auto_created_basket.delete()

【讨论】:

    【解决方案3】:

    您可以先将苹果从auto_created_basket 移动到existing_basket,然后再删除篮子:

    >>> auto_created_basket.apple_set.update(basket=existing_basket)
    >>> auto_created_basket.delete()
    

    >>> myapple.basket = existing_basket
    >>> myapple.save()
    >>> auto_created_basket.delete()
    

    或者,如果您想在单个篮子中收集已删除篮子中的苹果,您可以为on_delete 属性分配一个函数,如下所示:

    def get_sentinel_basket():
        basket, created = Basket.objects.get_or_create(name='DELETED')
        return basket
    

    --

    basket = models.ForeignKey(Basket, on_delete=models.SET(get_sentinel_basket))
    

    因此,当一个篮子被删除时,该篮子中苹果的.basket 属性将自动设置为Basket(name='DELETED')

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-08
      • 2021-05-23
      相关资源
      最近更新 更多