Django Admin:has_delete_permission忽略"删除"操作

Nic*_*ick 12 django django-admin

假设我有一个模型,其中ID为1的行是特殊的,不应该被删除,但所有其他行都可以删除.这是我尝试实现该逻辑:

models.py

from django.db import models


class Widget(models.Model):
    name = models.CharField(max_length=255)

    class Meta:
        ordering = ('name',)

    def __unicode__(self):
        return self.name
Run Code Online (Sandbox Code Playgroud)

admin.py

from django.contrib import admin

from .models import Widget


class WidgetAdmin(admin.ModelAdmin):
    def has_delete_permission(self, request, obj=None):
        return obj is None or obj.pk != 1

admin.site.register(Widget, WidgetAdmin)
Run Code Online (Sandbox Code Playgroud)

当上面的代码去除的变化形式"删除"按钮obj.pk1,这是我想要的.但是,在更改列表中,如果我选中ID为1的行的复选框,然后使用"删除所选小部件"操作,我可以删除该行.我想阻止它,但仍然允许使用"删除所选小部件"操作删除所有其他行.我怎样才能做到这一点?

Ant*_*ard 12

根据has_delete_permission文档字符串:

def has_delete_permission(self, request, obj=None):
    """
    Returns True if the given request has permission to change the given
    Django model instance, ...
    """
Run Code Online (Sandbox Code Playgroud)

这意味着has_delete_permission每个请求执行,而不是每个对象.在批量操作上,obj未设置.但是你可以检查request:

def has_delete_permission(self, request, obj=None):
    if request.POST and request.POST.get('action') == 'delete_selected':
        return '1' not in request.POST.getlist('_selected_action')
    return obj is None or obj.pk != 1
Run Code Online (Sandbox Code Playgroud)

请注意,上述工程,因为delete_selected动作需要has_delete_permission考虑.

您可能还想提供有关错误的一些详细信息:

from django.contrib import messages

def has_delete_permission(self, request, obj=None):
    if request.POST and request.POST.get('action') == 'delete_selected':
        if '1' in request.POST.getlist('_selected_action'):
            messages.add_message(request, messages.ERROR, (
                "Widget #1 is protected, please remove it from your selection "
                "and try again."
            ))
            return False
        return True
    return obj is None or obj.pk != 1
Run Code Online (Sandbox Code Playgroud)

我认为has_delete_permission出于性能原因,每个请求而不是每个对象都会调用它.在一般情况下,在运行查询之前进行SELECT查询和循环has_delete_permission(根据其工作可能是耗时的)是没用的DELETE.当与此相关时,由开发人员采取必要的步骤.


Kev*_*nry 9

您可以delete_selected使用自己的操作替换管理员的操作实现.就像是:

from django.contrib.admin import actions

class WidgetAdmin(admin.ModelAdmin):
    actions = [delete_selected]

    def delete_selected(self, request, queryset):
        # Handle this however you like. You could raise PermissionDenied,
        # or just remove it, and / or use the messages framework...
        queryset = queryset.exclude(pk=1)

        actions.delete_selected(self, request, queryset)
    delete_selected.short_description = "Delete stuff"
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅文档.