August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
Django给admin添加Action的方法实例详解
发布:2021年03月10日 | 作者:augustrush | 阅读量: 895

在使用Django自带的admin后台的时候,他提供了一些默认的指令可以对数据进行操作, 比如批量删除,修改等。同样的我们也可以添加自己的指令。

Django的admin允许您编写和注册“actions”--调用这些函数的对象列表是在更改列表页面上选择的。

为此,admin提供了自定义功能函数actions的手段,可以批量对数据进行修改。admin内置了一个批量删除对象的操作,如下图所示:

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

首先定义一下模型类:

from django.db import models

STATUS_CHOICES = (
    ('d', '草稿'),
    ('p', '已发表'),
    ('w', '撤回'),
)

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


编写action函数

action函数必须携带三个参数:

  • modeladmin: 当前的ModelAdmin
  • request: 当前的HttpRequest对象
  • queryset: 被选择的对象集

admin.py中编写action函数:

def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

注意:

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

for obj in queryset:
    do_something_with(obj)

到这里admin站点默认显示的是函数名称为标题的操作选项。可以通过action函数名.short_description来设置操作选项的标题:

make_published.short_description = "更新状态为已发表"

Django 3.2的新特性:

action()装饰器的description参数等价于在以前的版本中直接在action函数后设置short_description属性。为了兼容,目前仍然支持直接设置属性。

from django.contrib import admin

...

@admin.action(description="Mark selected stories as published")
def make_published(modeladmin, request, queryset):
    queryset.update(status='p')


向ModelAdmin中添加操作

admin.py中添加ModelAdmin:

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'body', 'status')

    actions = (make_published,)

Django 3.2的新特性

from django.contrib import admin
from myapp.models import Article

@admin.action(description='Mark selected stories as published')
def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'status']
    ordering = ['title']
    actions = [make_published]

admin.site.register(Article, ArticleAdmin)


处理action函数中的错误

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

def make_published(modeladmin, request, queryset):
    queryset.update(status="p")
    modeladmin.message_user(request, '更改成功')


make_published.short_description = "更新状态为已发表"


将自定义action函数添加到对应的ModelAdmin中

上面的示例显示 make_published 定义为函数的操作。这很好,但从代码设计的角度来看并不完美:因为动作与 Article 对象相关,将操作挂接到 ArticleAdmin 对象本身。

你可以这样做:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'body', 'status')

    fields = ('title', 'body', 'status')

    actions_on_bottom = True
    actions_on_top = True

    actions = (make_published, 'make_withdrawn')

    def make_withdrawn(self, request, queryset):
        queryset.update(status="w")
        self.message_user(request, "操作成功")

    make_withdrawn.short_description = "更新状态为撤销"

首先注意我们新写了一个类方法 make_withdrawn 并将其重命名为 modeladmin 参数到 self 第二,我们现在把字符串 'make_published' 在里面 actions 而不是直接的函数引用。这告诉了 ModelAdmin将操作作为一种方法进行查找。


跳转到中间页面

默认情况下,执行操作后,用户将被重定向回原始更改列表页面。但是,有些操作,尤其是更复杂的操作,需要返回中间页。例如,内置的删除操作要求在删除选定对象之前进行确认。

要实现这个功能,只需要在action方法中返回一个HttpResponse(或它的子类)。 例如下面是一个通过csv模块导出csv文件的例子:

exportable_fields = ('username', 'city', 'degree', 'first_score', 'first_result',
                     'first_interviewer_user', 'second_score', 'second_result', 'second_interviewer',
                     'hr_score', 'hr_result', 'hr_interviewer', "last_editor")


def export_model_as_csv(modeladmin, request, queryset):
    """
    :param modeladmin: 当前的ModelAdmin
    :param request: 当前的HttpRequest对象(即request)
    :param queryset: 选中数据的列表  被选择的对象(即QuerySet)
    :return:
    """
    response = HttpResponse(content_type="text/csv")
    field_list = exportable_fields
    response['Content-Disposition'] = 'attachment; filename=recruitment-candidates-list-%s.csv' % (
        datetime.now().strftime("%Y-%m-%d-%H-%M-%S"),
    )
    writer = csv.writer(response)

    # 以字段的verbose_name为值插入表头
    writer.writerow(
        [queryset.model._meta.get_field(f).verbose_name.title() for f in field_list]
    )

    # 插入数据
    for obj in queryset:
        csv_line_values = []
        for field in field_list:
            field_object = queryset.model._meta.get_field(field)
            field_value = field_object.value_from_object(obj)
            csv_line_values.append(field_value)
        writer.writerow(csv_line_values)

    return response


export_model_as_csv.short_description = "导出为CSV文件"

一般来说,像上面这样的东西并不被认为是一个好主意。大多数时候,最好的做法是返回一个HttpResponseRedirect并将用户重定向到你编写的视图,在Get查询字符串中传递所选对象的列表。这样你就可以在中间页面上提供复杂的交互逻辑。

Django默认执行完动作后会返回到当前页面并刷新,同样我们也可以自定义跳转到其他页面。 只需在动作函数上返回一个HttpResponse即可。

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ('title', 'body', 'status')

    fields = ('title', 'body', 'status')

    actions_on_bottom = True
    actions_on_top = True

    actions = (make_published, 'make_withdrawn', 'redirect_to_job_list')

    def make_withdrawn(self, request, queryset):
        queryset.update(status="w")
        self.message_user(request, "操作成功")

    def redirect_to_job_list(self, request, queryset):
        # 业务逻辑...
        return redirect('/joblist') # 此处使用的转发,一可以直接使用HttpResponse

    make_withdrawn.short_description = "更新状态为撤销"


编写可用于整个admin站点的action函数

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

AdminSite.add_action(action, name=None)[source]

可以通过第二个参数显式地给这个动作起一个名字

from django.contrib import admin

admin.site.add_action(export_selected_objects, '导出所选项目')


禁用action函数

有时候,对于某些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()方法。

这将返回允许的操作字典。键是操作名,值是 (function, name, short_description) 元组。

{
    'delete_selected': (<function delete_selected at 0x7f8c6750d0e0>, 'delete_selected', '删除所选的 %(verbose_name_plural)s'), 
    '更新状态为已发表': (<function make_published at 0x7f8c678bdcb0>, '更新状态为已发表', '更新状态为已发表'),            'make_withdrawn': (<function ArticleAdmin.make_withdrawn at 0x7f8c678e2170>, 'make_withdrawn', '更新状态为撤销'), 
    'redirect_to_job_list': (<function ArticleAdmin.redirect_to_job_list at 0x7f8c678e2200>, 'redirect_to_job_list', 'Redirect to job list')
}

例如,如果只希望admin能够批量删除对象:

class MyModelAdmin(admin.ModelAdmin):
    ...
    def get_actions(self, request):
        actions = super(ArticleAdmin, self).get_actions(request)
        print(actions)
        if request.user.username != 'admin':
            if 'delete_selected' in actions:
                del actions['delete_selected']
        return actions


设置action的权限

通过使用action函数名.allowed_permissions来限定用户行为。

def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

make_published.allowed_permissions = ('change',)

Django 3.2新特性

通过使用action()装饰器包装action函数并传递permissions参数,actions可以限制它们对具有特定权限的用户的可用性。

@admin.action(permissions=['change'])
def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

make_published()函数将只提供给通过ModelAdmin.has_change_permission()检查的用户。

如果permissions具有多个权限,只要用户通过至少一个检查,该操作就可用。

权限的可用值和相应的方法检查是:

  • 'add': ModelAdmin.has_add_permission()
  • 'change': ModelAdmin.has_change_permission()
  • 'delete': ModelAdmin.has_delete_permission()
  • 'view': ModelAdmin.has_view_permission()

也可以自定义其他相应的权限检查,只要在ModelAdmin上实现一个相应的has_\<value>_permission(self, request)

例如:

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

    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))
    make_published.short_description = "make stories published"
    make_published.allowed_permissions = ('publish',)


  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

版权所有 © 2020-2021 August Rush

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed