第二十一课 Django 自定义Admin actions

第二十一课 自定义Admin actions

通常情况下,admin的工作模式是“选中目标,然后修改目标”,但在同时修改大量目标的时候,这种模式就变得重复、繁琐。为此,admin提供了自定义功能函数actions的手段,可以批量对数据进行修改。

admin内置了一个批量删除对象的操作,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vjj13OhP-1608307641269)(/home/pyvip/py_case/python 笔记/图片/1762677-20201006092325558-1263831993.png)]

下面以一个新闻应用的文章模型为例,介绍一个批量更新的自定义actions,它将选择的文章由“草稿”状态更新为“发布”状态:

首先是模型的代码:


from django.db import models



STATUS_CHOICES = [

    ('d', 'Draft'),

    ('p', 'Published'),

    ('w', 'Withdrawn'),

]



class Article(models.Model):

    title = models.CharField(max_length=100)

    body = models.TextField()

    status = models.CharField(max_length=1, choices=STATUS_CHOICES)



    def __str__(self):

        return self.title

1. 编写action

action必须携带三个参数:

  • 当前的ModelAdmin

  • 当前的HttpRequest对象(即request)

  • 被选择的对象(即QuerySet)

在应用中的admin.py文件中写入:


def make_published(modeladmin, request, queryset):

    queryset.update(status='p')

注意:这里我们作为例子,简单地使用了queryset自带的update()方法,它能批量操作。但在多数情况下,你要自己遍历queryset的每个元素,并编写具体的操作。也就是:


for obj in queryset:

    do_something_with(obj)

还可以设置一个简单易懂的简短描述(可以使用中文),用于代替生硬的函数名:


def make_published(modeladmin, request, queryset):

    queryset.update(status='p')

# 注意缩进,下面这句不在函数体内。

make_published.short_description = "Mark selected stories as published"

2. 添加action

关键是其中的actions = [make_published]这句。


from django.contrib import admin

from myapp.models import Article



def make_published(modeladmin, request, queryset):

    queryset.update(status='p')

make_published.short_description = "Mark selected stories as published"



class ArticleAdmin(admin.ModelAdmin):

    list_display = ['title', 'status']

    ordering = ['title']

    actions = [make_published]



admin.site.register(Article, ArticleAdmin)

然后,页面看起来是下面的样子(注意下拉框):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NXQVZ7b-1608307641270)(/home/pyvip/py_case/python 笔记/图片/1762677-20201006092333048-752460978.png)]

处理错误

这其中,如果能够预知在自定义的操作中可能产生的错误,可以通过django.contrib.admin.ModelAdmin.message_user()以友好的方式给予用户提示信息

3. 将action定义为ModelAdmin的方法

上面的make_published看起来已经不错了,但是我们一般会将它作为ModelAdmin的方法来使用。下面我们把它移到ArticleAdmin类中:


class ArticleAdmin(admin.ModelAdmin):

    ...



    actions = ['make_published']  # 请注意这里改成字符串引用了

    # 第一个参数变为self

    def make_published(self, request, queryset):

        queryset.update(status='p')

    make_published.short_description = "Mark selected stories as published"

这样做的好处是自定义方法可以直接访问类本身。例如下面使用self引用,为方法添加提示信息的功能:


from django.contrib import messages

from django.utils.translation import ngettext



class ArticleAdmin(admin.ModelAdmin):

    ...



    def make_published(self, request, queryset):

        updated = queryset.update(status='p')

        self.message_user(request, ngettext(

            '%d story was successfully marked as published.',

            '%d stories were successfully marked as published.',

            updated,

        ) % updated, messages.SUCCESS)

效果如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJk3eArX-1608307641271)(/home/pyvip/py_case/python 笔记/图片/1762677-20201006092358438-294777179.png)]

下面是message_user方法的定义:


def message_user(self, request, message, level=messages.INFO, extra_tags='',

                     fail_silently=False):

下面是ngettext的定义:


def ngettext(singular, plural, number):  # 单数、复数、数量

    return _trans.ngettext(singular, plural, number)

4. 跳转到中间页面

默认情况下,执行完actions后,浏览器会返回先前的修改列表页面。但有时候,一些复杂的action需要返回中间页面,例如内置的删除方法,在执行删除动作之前,会弹出一个删除确认页面。

要实现这个功能,只需要在action方法中返回一个HttpResponse(或它的子类)。 例如下面是一个利用Django内置的序列化函数将一个对象保存为json格式的范例:


from django.core import serializers

from django.http import HttpResponse



def export_as_json(modeladmin, request, queryset):

    response = HttpResponse(content_type="application/json")

    serializers.serialize("json", queryset, stream=response)

    return response

多数情况下,我们会使用HttpResponseRedirect跳转到一个中间页面,并在GET方法的url中携带别选择的对象作为参数传递过去,然后在这个新的视图中接收这个参数,并编写具体的更加复杂的业务逻辑,如下面的代码所示:


from django.contrib import admin

from django.contrib.contenttypes.models import ContentType

from django.http import HttpResponseRedirect



def export_selected_objects(modeladmin, request, queryset):

    # 获得被打钩的checkbox对应的对象id的列表

    selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)

    # 获取对应的模型

    ct = ContentType.objects.get_for_model(queryset.model)

    # 构造访问的url,使用GET方法,跳转到相应的页面

    return HttpResponseRedirect("/export/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))

# 或者这么写:

def export_selected_objects(modeladmin, request, queryset):

    selected = queryset.values_list('pk', flat=True)   # 注意这行

    ct = ContentType.objects.get_for_model(queryset.model)

    return HttpResponseRedirect('/export/?ct=%s&ids=%s' % (

        ct.pk,

        ','.join(str(pk) for pk in selected),

    ))

5. 编写可用于整个站点的action

前面创建的actions只能应用于绑定的模型。实际上有时候,我们还需要可以对admin站点内所有模型都有效的acitons。上面写的export_selected_objects函数可以是一个很好的例子。要实现这一功能,你需要使用内置的AdminSite.add_action方法:


AdminSite.add_action(action, name=None)


from django.contrib import admin



admin.site.add_action(export_selected_objects)

也可以为动作指定新的名字:


admin.site.add_action(export_selected_objects, 'export_selected')

6. 禁用acitons

有时候,对于某些actions,我们想全局禁用或者局部禁用它。需要使用AdminSite.disable_action(name)方法。

  • 禁用全站级别的acitons,例如禁用内置的删除方法:

admin.site.disable_action('delete_selected')

  • 全站禁用,但个别可用:在ModelAdmin.actions中显式地引用。例如:

# 全站禁用删除功能

admin.site.disable_action('delete_selected')



# 这个老老实实的被禁了

class SomeModelAdmin(admin.ModelAdmin):

    actions = ['some_other_action']

    ...



# 这个声明:我还要用

class AnotherModelAdmin(admin.ModelAdmin):

    actions = ['delete_selected', 'a_third_action']

    ...

  • 在指定模型中禁用所有actions:设置ModelAdmin.actions为None。(这会连带全局actions一起禁用了。)

    class MyModelAdmin(admin.ModelAdmin):

        actions = None

  • 根据条件自动启用或禁用:

还可以根据条件自动选择性的启动或禁用某些acitons,你只需要改写ModelAdmin.get_actions()方法。

该方法将返回一个包含actions的字典。字典的键是aciton的名字(也就是前面的’delete_selected’, 'a_third_action’之类),值是一个元组,包含(函数、名字、别名)

例如,允许用户名以“J”开头的用户批量删除对象,但其它用户不行:


class MyModelAdmin(admin.ModelAdmin):

    ...



    def get_actions(self, request):

        actions = super().get_actions(request)

        if request.user.username[0].upper() != 'J':

            if 'delete_selected' in actions:

                del actions['delete_selected']

        return actions

可以为action添加权限控制,方法是添加allowed_permissions属性:


def make_published(modeladmin, request, queryset):

    queryset.update(status='p')

make_published.allowed_permissions = ('change',)  # 看这里!

这样一来,只有能通过ModelAdmin.has_change_permission()方法进行权限检查的用户才可以执行make_published()动作。

如果allowed_permissions属性有多个权限设定,那么只需要具备其中一个权限就可以执行动作。

可设置的权限短语和对应的检查方法如下:

  • 'add': ModelAdmin.has_add_permission()

  • 'change': ModelAdmin.has_change_permission()

  • 'delete': ModelAdmin.has_delete_permission()

  • 'view': ModelAdmin.has_view_permission()

当然,你也可以自定义权限,如下所示:


from django.contrib import admin

from django.contrib.auth import get_permission_codename



class ArticleAdmin(admin.ModelAdmin):

    actions = ['make_published']



    def make_published(self, request, queryset):

        queryset.update(status='p')

    make_published.allowed_permissions = ('publish',)



    def has_publish_permission(self, request):

        """Does the user have the publish permission?"""

        opts = self.opts

        codename = get_permission_codename('publish', opts)

        return request.user.has_perm('%s.%s' % (opts.app_label, codename))

猜你喜欢

转载自blog.csdn.net/zly717216/article/details/111398738