我有两个模型,每个模型都有一个图像。一个人拥有父母的外键。当我删除父级时,我想删除父级和子级以及磁盘上的映像文件。为此,我重写了删除方法:
class MyModelParent(models.Model):
image = models.ImageField(upload_to = "images/" )
def delete(self, *args, **kwargs):
if self.image:
self.image.delete()
super(MyModelParent, self).delete(*args, **kwargs)
class MyModelChild(models.Model):
parent = models.ForeignKey(MyModelParent)
image = models.ImageField(upload_to = "images/" )
def delete(self, *args, **kwargs):
if self.image:
self.image.delete()
super(MyModelChild, self).delete(*args, **kwargs)
当我删除MyModelParent的实例时,会调用其重写的delete(),但不会调用其中的子节点(即使它们从数据库中删除),因此它们的图像仍保留在磁盘上。谁知道我做错了什么?
答案 0 :(得分:10)
你没有做错任何事。问题是任何子级的delete()
方法在级联时都不会被调用。
从delete
的文档(级联删除使用数据库查询):
delete()方法执行批量删除,不调用任何delete() 模型上的方法。但是,它会发出pre_delete和 post_delete为所有已删除对象发出信号(包括级联 缺失)。
但是,仍会发送pre_delete
和post_delete
信号。你需要做的是连接一个回调,它将监听这些信号之一并进行任何额外的清理工作。
有关connecting signals
的更多信息,请参阅相关文档。
答案 1 :(得分:2)
这种行为是由于(来自Making Queries | Deleting objects,在文档中)ForeignKey
's on_delete
关键字参数的CASCADE
选项(默认)仅设置数据库约束:
请记住,只要有可能,这将完全在SQL中执行,因此在此过程中不一定会调用单个对象实例的
delete()
方法。如果您在模型类上提供了自定义delete()
方法并希望确保调用它,则需要手动"删除该模型的实例(例如,通过迭代QuerySet
并单独调用每个对象上的delete()
),而不是使用delete()
的批量QuerySet
方法。当Django删除一个对象时,默认情况下它会模拟SQL约束
ON DELETE CASCADE
的行为 - 换句话说,任何具有指向要删除的对象的外键的对象都将随之删除。
答案 2 :(得分:1)
@ Nate的答案涵盖了问题的一部分,但只是低级部分。删除父级应该导致子级(具有父级的隐式外键)也会在级联中被删除,但是在这种情况下不会运行子级的delete()。
所有这一切都是正确的,但更重要的是,这是因为你要删除父母而不是孩子。在继承中,父母对孩子的方法一无所知,因此就他们而言,他们可能也不存在,因此被忽略。如果要运行自定义子方法,则必须从子节点而不是父节点运行它。