删除父项时,不会调用子模型的重写删除方法?

时间:2012-01-20 16:00:05

标签: django

我有两个模型,每个模型都有一个图像。一个人拥有父母的外键。当我删除父级时,我想删除父级和子级以及磁盘上的映像文件。为此,我重写了删除方法:

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(),但不会调用其中的子节点(即使它们从数据库中删除),因此它们的图像仍保留在磁盘上。谁知道我做错了什么?

3 个答案:

答案 0 :(得分:10)

你没有做错任何事。问题是任何子级的delete()方法在级联时都不会被调用。

delete的文档(级联删除使用数据库查询):

  

delete()方法执行批量删除,不调用任何delete()   模型上的方法。但是,它会发出pre_delete和   post_delete为所有已删除对象发出信号(包括级联   缺失)。

但是,仍会发送pre_deletepost_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()。

所有这一切都是正确的,但更重要的是,这是因为你要删除父母而不是孩子。在继承中,父母对孩子的方法一无所知,因此就他们而言,他们可能也不存在,因此被忽略。如果要运行自定义子方法,则必须从子节点而不是父节点运行它。