删除在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属性值明确规定通过防止删除篮子来保护苹果,即只要篮子里还有一个苹果,篮子就不能被删除。

正如官方文档所说的

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

PROTECT参数通过引发来阻止被引用对象的删除ProtectedError

因此,最简单的步骤应该是删除on_delete参数,并使用默认行为
basket = models.ForeignKey(Basket)

然而,请查看ForeignKey模型字段的所有可能参数,并选择适合您的应用程序/场景需求的组合。

更新:

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

我讨厌回答我的问题,但是我的例子太过于简化了。这些答案都是很好的观点。

实际产品中有post_save信号参与,例如负责auto_created_basket的创建。

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

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

最新更新