覆盖django的模型删除方法以进行批量删除

时间:2015-03-06 09:53:15

标签: django model bulk imagefield

我重写了Django的模型删除方法,以便删除磁盘中用于图像字段的孤立文件,如下所示:

class Image(models.Model):
    img = models.ImageField(upload_to=get_image_path)
    ...
    def delete(self, *args, **kwargs):
        self.img.delete()
        super(Image, self).delete(*args, **kwargs)

当我从管理员中删除单个对象时这很好用,但是当我选择多个对象并删除它们时,这似乎不会被调用。我已经谷歌搜索了一段时间,但没有找到合适的关键字来获得答案,官方文档似乎也没有谈论这个主题。

3 个答案:

答案 0 :(得分:33)

It does

  

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

要使其正常工作,您可以覆盖QuerySet上的删除方法,然后将QuerySet应用为经理:

class ImageQuerySet(models.QuerySet):

    def delete(self, *args, **kwargs):
        for obj in self:
            obj.img.delete()
        super(ImageQuerySet, self).delete(*args, **kwargs)

class Image(models.Model):
    objects = ImageQuerySet.as_manager()
    img = models.ImageField(upload_to=get_image_path)
    ...
    def delete(self, *args, **kwargs):
        self.img.delete()
        super(Image, self).delete(*args, **kwargs)

答案 1 :(得分:7)

queryset的删除方法直接在数据库上工作。它不会调用Model.delete()方法。来自docs

  

请记住,只要有可能,这将完全在SQL中执行,因此在此过程中不一定会调用各个对象实例的delete()方法。如果您在模型类上提供了自定义delete()方法并希望确保调用它,则需要“手动”删除该模型的实例(例如,通过迭代QuerySet并调用delete()on每个对象单独使用)而不是使用QuerySet的批量delete()方法。

如果要覆盖Django管理界面的默认行为,可以编写自定义delete操作:

https://docs.djangoproject.com/en/1.7/ref/contrib/admin/actions/

另一种方法是覆盖post_delete(或pre_delete)信号,而不是delete方法:

https://docs.djangoproject.com/en/1.7/ref/signals/#django.db.models.signals.post_delete

  

与pre_delete类似,但在模型的delete()方法和queryset的delete()方法结束时发送

答案 2 :(得分:0)

我相信docs

中已解决了此问题

它说:

  

在批量操作中不会调用覆盖的模型方法

     

请注意,使用QuerySet批量删除对象或级联删除操作时,不必调用对象的delete()方法。为了确保执行自定义的删除逻辑,您可以使用pre_delete和/或post_delete信号。

     

不幸的是,批量创建或更新对象时没有解决方法,因为没有调用save(),pre_save和post_save。

如以上文档中所建议,我相信更好的解决方案是使用post_delete信号,如下所示:

from django.db.models.signals import post_delete
from django.dispatch import receiver

class Image(models.Model):
    img = models.ImageField(upload_to=get_image_path)
    ...

@receiver(post_delete, sender=Image)
def delete_image_hook(sender, instance, using, **kwargs):
    instance.img.delete()

与覆盖delete方法不同的是,delete_image_hook函数也应在批量删除和级联删除时调用。以下是有关使用Django的信号的更多信息:https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-to-signals-sent-by-specific-senders

关于先前答案的说明: 之前的一些帖子建议重写QuerySet的delete方法,这可能会影响性能和其他意外行为。也许这些答案是在实施Django的Signals之前写的,但是我认为使用Signals是一种更干净的方法。