28

我有 admin.py 如下:

  class profilesAdmin(admin.ModelAdmin):
     list_display = ["type","username","domain_name"]

现在我想在删除对象之前执行一些操作:

  class profilesAdmin(admin.ModelAdmin):
     list_display = ["type","username","domain_name"]

     @receiver(pre_delete, sender=profile)
     def _profile_delete(sender, instance, **kwargs):
        filename=object.profile_name+".xml"
        os.remove(os.path.join(object.type,filename))

如果我使用这样的删除信号方法,我会收到一个错误,说self应该是第一个参数。

如何修改上述功能?
我想检索被删除对象的 profile_name。如何才能做到这一点?

我还尝试了覆盖 delete_model 方法:

def delete_model(self, request, object):
    filename=object.profile_name+".xml"
    os.remove(os.path.join(object.type,filename))
    object.delete()

但是,如果必须一次删除多个对象,则此方法不起作用。

4

5 回答 5

26

您可以使用来自Django 2.1的delete_queryset用于批量删除对象,使用delete_model用于单次删除。这两种方法都会在删除对象之前处理一些事情。

ModelAdmin.delete_queryset(请求,查询集)


这是Django 2.1发行说明中关于delete_queryset的解释。

为 delete_queryset() 方法提供了 HttpRequest 和要删除的对象的 QuerySet。重写此方法,自定义“删除选定对象”的删除过程</p>

我们来看看delete_queryset是做什么的,可以覆盖admin。ModelAdmin类以这种方式通过包含delete_queryset函数。在这里,您将获得对象列表,queryset.delete()意味着一次删除所有对象,或者您可以添加一个循环来逐个删除。

def delete_queryset(self, request, queryset):
    print('==========================delete_queryset==========================')
    print(queryset)

    """
    you can do anything here BEFORE deleting the object(s)
    """

    queryset.delete()

    """
    you can do anything here AFTER deleting the object(s)
    """

    print('==========================delete_queryset==========================')

所以我要从“选择窗口”中删除 5 个对象,这就是这 5 个对象。

删除 5 个对象

然后你会像这样重定向到确认页面,

将删除这 5 个对象

请记住“是的,我确定”按钮,稍后我会解释它。当您单击该按钮时,您将在删除这 5 个对象后看到下图。

成功删除 5 个对象

这是终端输出,

这 5 个对象的终端输出

因此,您将获得这 5 个对象作为 QuerySet 列表,在删除之前,您可以在评论区做任何您想做的事情。

ModelAdmin.delete_model(request, obj)


这是关于delete_model的解释。

delete_model 方法被赋予了 HttpRequest 和一个模型实例。覆盖此方法允许执行删除前或删除后操作。调用 super().delete_model() 以使用 Model.delete() 删除对象。

我们看看delete_model做了什么,可以覆盖admin。ModelAdmin类以这种方式通过包含delete_model函数。

actions = ['delete_model']

def delete_model(self, request, obj):
    print('============================delete_model============================')
    print(obj)

    """
    you can do anything here BEFORE deleting the object
    """

    obj.delete()

    """
    you can do anything here AFTER deleting the object
    """

    print('============================delete_model============================')

我只需单击我的第 6 个对象即可从“更改窗口”中删除。

从中删除 1 个对象

还有另一个删除按钮,当您单击它时,您会看到我们之前看到的窗口。

将删除那 1 个对象

单击“是,我确定”按钮删除单个对象。您将看到以下窗口,其中包含有关已删除对象的通知。

成功删除 1 个对象

这是终端输出,

那些 1 个对象的终端输出

因此,您将获得选定的对象作为单个 QuerySet 并且在删除之前,您可以在评论区域中做任何您想做的事情。


最后的结论是,您可以使用delete_querysetdelete_model在 Django 管理站点中单击“选择窗口”或“更改窗口”中的“是的,我确定”按钮来处理删除事件。这样我们就不需要处理像django.db.models.signals.pre_deletedjango.db.models.signals.post_delete这样的信号了。

这是完整的代码,

from django.contrib import admin

from . import models

class AdminInfo(admin.ModelAdmin):
    model = models.AdminInfo
    actions = ['delete_model']

    def delete_queryset(self, request, queryset):
        print('========================delete_queryset========================')
        print(queryset)

        """
        you can do anything here BEFORE deleting the object(s)
        """

        queryset.delete()

        """
        you can do anything here AFTER deleting the object(s)
        """

        print('========================delete_queryset========================')

    def delete_model(self, request, obj):
        print('==========================delete_model==========================')
        print(obj)

        """
        you can do anything here BEFORE deleting the object
        """

        obj.delete()

        """
        you can do anything here AFTER deleting the object
        """

        print('==========================delete_model==========================')

admin.site.register(models.AdminInfo, AdminInfo)
于 2019-05-16T09:44:44.380 回答
20

你用你的delete_model方法走在正确的轨道上。当 django 管理员一次对多个对象执行操作时,它使用更新功能。但是,正如您在文档中看到的,这些操作仅使用 SQL 在数据库级别执行。

您需要将您的delete_model方法添加为 django 管理员中的自定义操作

def delete_model(modeladmin, request, queryset):
    for obj in queryset:
        filename=obj.profile_name+".xml"
        os.remove(os.path.join(obj.type,filename))
        obj.delete()

然后将您的功能添加到您的模型管理员 -

class profilesAdmin(admin.ModelAdmin):
    list_display = ["type","username","domain_name"]
    actions = [delete_model]
于 2013-03-04T07:27:40.687 回答
6

主要问题是 Django 管理员的批量删除使用 SQL,而不是 instance.delete(),如其他地方所述。对于仅限管理员的解决方案,以下解决方案保留了 Django 管理员的“你真的要删除这些”插页式广告。

最通用的解决方案是覆盖模型管理器返回的查询集以拦截删除。

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, admin.ModelAdmin):
    ...
于 2014-10-03T15:35:50.057 回答
1

你的方法应该是

class profilesAdmin(admin.ModelAdmin):
    #...

    def _profile_delete(self, sender, instance, **kwargs):
        # do something

    def delete_model(self, request, object):
        # do something

您应该在每个方法签名(通常称为self)中添加对当前对象的引用作为第一个参数。此外,delete_model 应该作为一种方法来实现。

于 2013-03-04T07:34:24.850 回答
1

您尝试覆盖 delete_model 方法失败,因为当您删除 django 使用的多个对象时QuerySet.delete(),出于效率原因,您的模型delete()方法将不会被调用。

你可以在那里看到它https://docs.djangoproject.com/en/1.9/ref/contrib/admin/actions/
观看开头的警告

管理员delete_model()与模型的delete() https://github.com/django/django/blob/master/django/contrib/admin/options.py#L1005相同

所以当你删除多个对象时,你自定义的 delete 方法永远不会被调用。

你有两种方法。

1.自定义删除动作。
为每个选定项目调用 Model.delete() 的操作。

2.使用信号。
您可以单独使用信号,而不是在课堂内。

你也可以看这个问题Django model: delete() not trigger

于 2016-03-19T15:34:14.770 回答