删除在 PROTECTED ForeignKey 下自动创建的模型

Deleting auto created model which is under PROTECTED ForeignKey

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>])

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

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

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

正如official docs所说

When an object referenced by a ForeignKey is deleted, Django by default emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.

the PROTECT parameter prevents deletion of the referenced object by raising ProtectedError

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

basket = models.ForeignKey(Basket)

但是,请查看 ForeignKey model field 的所有可能参数,然后选择适合您的 application/scenario 要求的组合。

更新:

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

您可以先尝试将苹果从 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')

我不想回答我的问题,但我的例子过于简单化了。回答的很到位。

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

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