在使用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函数必须携带三个参数:
在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')
在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)
这其中,如果你能够预知在自定义的操作中可能产生的错误,请处理该错误,并通过django.contrib.admin.ModelAdmin.message_user()
以友好的方式给予用户提示信息。
def make_published(modeladmin, request, queryset):
queryset.update(status="p")
modeladmin.message_user(request, '更改成功')
make_published.short_description = "更新状态为已发表"
上面的示例显示 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 = "更新状态为撤销"
前面创建的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, '导出所选项目')
有时候,对于某些actions函数,我们想全局禁用或者局部禁用它。需要使用AdminSite.disable_action(name)
方法。
例如,禁用内置的删除方法:
admin.site.disable_action('delete_selected')
例如:
# 全站禁用删除功能
admin.site.disable_action('delete_selected')
# 这个老老实实的被禁了
class SomeModelAdmin(admin.ModelAdmin):
actions = ['some_other_action']
...
# 这个声明:我还要用
class AnotherModelAdmin(admin.ModelAdmin):
actions = ['delete_selected', 'a_third_action']
...
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函数名.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具有多个权限,只要用户通过至少一个检查,该操作就可用。
权限的可用值和相应的方法检查是:
也可以自定义其他相应的权限检查,只要在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构建