在Django中,如何删除特定实例时所有相关对象?
我最开始尝试重写 delete() 方法,但这对 QuerySet 的批量删除方法不起作用。这个问题应该和 pre_delete 信号有关,但我搞不清楚。我的代码如下:
def _pre_delete_problem(sender, instance, **kwargs):
instance.context.delete()
instance.stat.delete()
但是这个方法似乎被无限调用,导致程序陷入死循环。有人能帮帮我吗?
3 个回答
1
如果你想快速删除一个实例以及它所有相关的对象,还有那些相关对象的对象,等等,而不需要更改数据库的结构,你可以这样做 -
def recursive_delete(to_del):
"""Recursively delete an object, all of its protected related
instances, those instances' protected instances, and so on.
"""
from django.db.models import ProtectedError
while True:
try:
to_del_pk = to_del.pk
if to_del_pk is None:
return # unsaved object
to_del.delete()
print(f"Deleted {to_del.__class__.__name__} with pk {to_del_pk}: {to_del}")
except ProtectedError as e:
for protected_ob in e.protected_objects:
recursive_delete(protected_ob)
不过要小心哦!
我建议只在调试时使用这个方法,比如在一次性的脚本中(或者在命令行里),并且是在我不介意清空的测试数据库上。因为关系并不总是很明显,如果某些东西被保护了,可能是有原因的。
1
在你的情况下,使用post_delete信号而不是pre_delete可以解决无限循环的问题。因为外键的默认删除方式是级联删除(cascade),这样用pre_delete的逻辑就会导致instance.context对象调用instance的删除,这样又会再调用instance.context,结果就一直循环下去了。
使用这种方法:
def _post_delete_problem(sender, instance, **kwargs):
instance.context.delete()
instance.stat.delete()
post_delete.connect(_post_delete_problem, sender=Foo)
可以完成你想要的清理工作。