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

Kil*_*elo 18 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)
Run Code Online (Sandbox Code Playgroud)

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

Gwy*_*idD 36

它确实:

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)
Run Code Online (Sandbox Code Playgroud)


Sel*_*cuk 7

查询集的删除方法直接在数据库上工作.它不会调用Model.delete()方法.来自文档:

请记住,只要有可能,这将完全在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()方法结束时发送.


mod*_*tos 5

我相信这个问题已在文档中解决

它说:

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

请注意,当使用 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()
Run Code Online (Sandbox Code Playgroud)

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

注意以前的答案: 一些较早的帖子建议覆盖deleteQuerySet的方法,这可能会影响性能和其他意外行为。也许这些答案是在 Django 的 Signals 实现之前写的,但我认为使用 Signals 是一种更简洁的方法。