【问题标题】:Django model: delete() not triggeredDjango 模型:delete() 未触发
【发布时间】:2010-12-01 02:39:04
【问题描述】:

我有一个模型:

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

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

【问题讨论】:

  • 也许您没有删除任何内容?你能告诉我们你在调用什么 delete() 吗?
  • 我只是在管理区删了一个项目试了一下,没有手动调用。

标签: django django-models


【解决方案1】:

我认为您可能正在使用管理员的批量删除功能,并且遇到管理员的批量删除方法不调用 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)

【讨论】:

  • 是的,就是这样,非常感谢。您能简要解释一下您的自定义管理方法是什么样的吗?
  • 顺便说一句 - 可能有更优雅的方式来完成它,但它确实有效!
  • 这很好用。我注意到的一件事是这不会显示“你确定吗?”以前存在的页面。有什么想法吗?
  • @mikec - 去看看code.djangoproject.com/browser/django/trunk/django/contrib/… - 你可能只是覆盖你需要的位(当前第47行 -queryset.delete()) - 使用类似于扩展通用视图的建议( b-list.org/weblog/2006/nov/16/…) - 即编写一个方法来进行初始设置和处理帖子,否则,只需委托给django.contrib.admin.actions.delete_selected。如果这没有意义,请提出问题以获得更多帮助,并在此评论线程中联系我。
  • @Greg - 我提到的票是修复文档,现在已经完成(看看docs.djangoproject.com/en/dev/ref/contrib/admin/actions上的警告)。
【解决方案2】:

我知道这个问题很古老,但我又遇到了这个问题,想补充一点,您可以随时将代码移动到 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 开始)。

【讨论】:

  • 非常漂亮。只是想知道在 django 1.4 中有什么改进吗?
  • 谢谢一吨!我使用 post_delete 来避免递归删除。
【解决方案3】:

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

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

ma​​nagers.py 中:

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

class PhotoQuerySet(QuerySet):
    """ 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()

models.py 中:

from django.db import models

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

    objects = PhotoQuerySet.as_manager()

    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()

【讨论】:

  • 在我尝试捕获和修改 Django 管理员中批量删除操作的行为的所有替代方案中,这是唯一一个真正有效的方案!
  • 不过,有一点可以通过移除 Mixin 并仅在 QuerySet 类中实现 delete 方法来简化(至少在 Django 2.1 上),它会工作得很好。
  • @SebastiánVansteenkiste 感谢您提及这一点。我已使用QuerySet.as_manager() 将代码示例更新为现代 Django 样式。确实不需要 mixins。
  • 不错!我不知道存在语法!我检查了文档,它甚至已经存在于 2.1 中!
【解决方案4】:

使用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 方法。由于不再是批量操作,因此效率较低,但它可以保持文件系统清洁 =)

【讨论】:

  • 这是 Django 最新版本的正确答案恕我直言。谢谢!
  • 这个答案对我有用。我正在使用 Django 2.1
【解决方案5】:

主要问题是 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):
    ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 2014-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多