Django模型:delete()未触发

时间:2009-09-24 14:05:28

标签: django django-models

我有一个模特:

class MyModel(models.Model):
 ...
    def save(self):
        print "saving"
        ...
    def delete(self):
        print "deleting"
        ...

save() - 方法被触发,但delete()不被触发。我使用最新的svn-Version(Django版本1.2 pre-alpha SVN-11593),关于http://www.djangoproject.com/documentation/models/save_delete_hooks/的文档,这应该可行。 有什么想法吗?

5 个答案:

答案 0 :(得分:71)

我认为您可能正在使用管理员的批量删除功能,并且遇到管理员的批量删除方法未调用delete()这一事实(请参阅相关的ticket)。

我过去通过编写自定义管理操作来删除模型来解决这个问题。

如果您没有使用管理员的批量删除方法(例如,您点击了对象编辑页面上的删除按钮),那么还会有其他内容。

请参阅警告here

  

“删除所选对象”操作   使用QuerySet.delete()来提高效率   原因,这一点很重要   警告:您的模型的delete()方法   将不会被召唤。

     

如果您希望覆盖此行为,   简单地写一个自定义动作   完成你的删除   首选方式 - 例如,通过   为每个人打电话Model.delete()   选定的项目。

     

有关批量删除的更多背景信息,   请参阅object deletion上的文档。

我的自定义管理模型如下所示:

from photoblog.models import PhotoBlogEntry
from django.contrib import admin    

class PhotoBlogEntryAdmin(admin.ModelAdmin):
    actions=['really_delete_selected']

    def get_actions(self, request):
        actions = super(PhotoBlogEntryAdmin, self).get_actions(request)
        del actions['delete_selected']
        return actions

    def really_delete_selected(self, request, queryset):
        for obj in queryset:
            obj.delete()

        if queryset.count() == 1:
            message_bit = "1 photoblog entry was"
        else:
            message_bit = "%s photoblog entries were" % queryset.count()
        self.message_user(request, "%s successfully deleted." % message_bit)
    really_delete_selected.short_description = "Delete selected entries"

admin.site.register(PhotoBlogEntry, PhotoBlogEntryAdmin)

答案 1 :(得分:34)

我知道这个问题很古老,但我刚刚再遇到这个问题,并希望补充一点,您可以随时将代码移动到pre_delete或post_delete信号,如下所示:

from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver

@receiver(pre_delete, sender=MyModel)
def _mymodel_delete(sender, instance, **kwargs):
    print "deleting"

它适用于管理员的批量删除操作(至少从1.3.1开始)。

答案 2 :(得分:6)

管理员的批量操作调用queryset.delete()

您可以覆盖查询集的.delete()方法, 所以它总是对对象进行1对1的删除。例如:

managers.py

中的

from django.db import models
from django.db.models.query import QuerySet

class PhotoQueryMixin(object):
    """ Methods that appear both in the manager and queryset. """
    def delete(self):
        # Use individual queries to the attachment is removed.
        for photo in self.all():
            photo.delete()

class PhotoQuerySet(PhotoQueryMixin, QuerySet):
    pass

class PhotoManager(PhotoQueryMixin, models.Manager):
    def get_query_set(self):
        return PhotoQuerySet(self.model, using=self._db)

models.py

from django.db import models

class Photo(models.Model):
    image = models.ImageField(upload_to='images')

    objects = PhotoManager()

    def delete(self, *args, **kwargs):
        # Note this is a simple example. it only handles delete(),
        # and not replacing images in .save()
        super(Photo, self).delete(*args, **kwargs)
        self.image.delete()

答案 3 :(得分:3)

主要问题是Django管理员的批量删除使用SQL,而不是其他地方提到的instance.delete()。对于仅限管理员的解决方案,以下解决方案保留了Django管理员"你真的想要删除这些"间质性。然而,vdboor的解决方案是最普遍的。

from django.contrib.admin.actions import delete_selected

class BulkDeleteMixin(object):
    class SafeDeleteQuerysetWrapper(object):
        def __init__(self, wrapped_queryset):
            self.wrapped_queryset = wrapped_queryset

        def _safe_delete(self):
            for obj in self.wrapped_queryset:
                obj.delete()

        def __getattr__(self, attr):
            if attr == 'delete':
                return self._safe_delete
            else:
                return getattr(self.wrapped_queryset, attr)

        def __iter__(self):
            for obj in self.wrapped_queryset:
                yield obj

        def __getitem__(self, index):
            return self.wrapped_queryset[index]

        def __len__(self):
            return len(self.wrapped_queryset)

    def get_actions(self, request):
        actions = super(BulkDeleteMixin, self).get_actions(request)
        actions['delete_selected'] = (BulkDeleteMixin.action_safe_bulk_delete, 'delete_selected', ugettext_lazy("Delete selected %(verbose_name_plural)s"))
        return actions

    def action_safe_bulk_delete(self, request, queryset):
        wrapped_queryset = BulkDeleteMixin.SafeDeleteQuerysetWrapper(queryset)
        return delete_selected(self, request, wrapped_queryset)


class SomeAdmin(BulkDeleteMixin, ModelAdmin):
    ...

答案 4 :(得分:1)

使用django v2.2.2,我通过以下代码解决了这个问题

models.py

class MyModel(models.Model):
    file = models.FileField(upload_to=<path>)

    def save(self, *args, **kwargs):
        if self.pk is not None:
            old_file = MyModel.objects.get(pk=self.pk).file
            if old_file.path != self.file.path:
                self.file.storage.delete(old_file.path)

        return super(MyModel, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        ret = super(MyModel, self).delete(*args, **kwargs)
        self.file.storage.delete(self.file.path)
        return ret

admin.py

class MyModelAdmin(admin.ModelAdmin):

    def delete_queryset(self, request, queryset):
        for obj in queryset:
            obj.delete()

对于DefaultAdminSite,如果用户具有正确的权限,则会调用delete_queryset,唯一的区别是原始函数调用queryset.delete()不会触发模型delete方法。由于不再是批量操作,所以效率较低,但是可以保持文件系统干净=)