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')
。