简单来说,Django管理员的基本工作流程是“选择一个对象,然后进行更改”。这对大多数用例都很有效。 然而当你一次性要对多个对象做相同的改变,这个流程是非常的单调乏味的。
在这些情况下,Django Admin 可以让你编写并注册“Action” —— 仅仅只是一个以更改列表页面上选中对象的列表为参数的回调函数。
如果您查看管理员中的任何更改列表,您将看到此功能在操作中; Django附带所有型号的“删除所选对象”操作。 例如,下面是从Django 内建的django.contrib.auth
应用创建的用户模型:
警告
“delete selected objects” Action 由于性能因素使用了QuerySet.delete()
,这里有个附加说明:它不会调用你模型的delete()
方法。
如果你想覆写这一行为,编写自定义Action,以你的方式实现删除就可以了 -- 例如,对每个已选择的元素调用Model.delete()
。
关于整体删除的更多信息,参见object deletion的文档。
继续阅读,来弄清楚如何向列表添加你自己的Action。
通过示例来解释Action 最为简单,让我们开始吧。
Action 的一个最为普遍的用例是模型的整体更新。 考虑带有Article
模型的简单新闻应用:
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): # __unicode__ on Python 2
return self.title
我们可能在模型上执行的一个普遍任务是,将文章状态从“草稿”更新为“已发布”。 在Admin 界面上一次处理一篇文章非常轻松,但是如果我们想要批量发布一些文章,则会非常单调乏味。 所以让我们编写一个Action,可以让我们将一篇文章的状态修改为“已发布”。
首先,我们需要定义一个函数,当在Admin 界面上触发该Action 的时候调用。 Action 函数,跟普通的函数一样,需要接收三个参数:
ModelAdmin
HttpRequest
QuerySet
我们用于发布这些文章的函数并不需要ModelAdmin
和请求对象,但是我们会用到查询集:
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
注
为了性能最优,我们使用查询集的update method。 其他类型的动作可能需要单独处理每个对象;在这些情况下,我们将迭代查询:
for obj in queryset:
do_something_with(obj)
编写Action 的全部内容实际上就这么多了。 但是,我们要进行一个可选但是有用的步骤,在Admin 中给Action 起一个“友好”的标题。
默认情况下,Action 以“Make published” 的形式出现在Action 列表中 —— 将函数名称中的所有下划线用空格替换。 这样就很好了,但是我们可以提供一个更好、更人性化的名称,通过向make_published
函数添加short_description
属性:
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
注
这可能看起来很熟悉管理员的list_display
选项使用相同的技术来为在那里注册的回调函数提供可读的描述。
ModelAdmin
¶添加动作接下来,我们需要把Action 告诉ModelAdmin
。 它和其他配置项的工作方式相同。 所以,带有Action 及其注册的完整的admin.py
看起来像这样:
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)
这段代码向我们提供的Admin 更改列表看起来像这样:
这就是全部内容了。 如果你想编写自己的Action ,你现在应该知道怎么开始了。 这篇文档的剩余部分会介绍更多高级技巧。
如果你的Action 运行时发生可预见的些错误,你应该以优雅的方式向用户通知这些错误。 这意味着处理异常并使用django.contrib.admin.ModelAdmin.message_user()
在响应中向用户展示友好的问题描述。
对于进一步的选择,你可以使用一些额外的选项。
ModelAdmin
方法¶的操作上面的例子展示了定义为一个简单函数的make_published
操作。 这真是极好的,但是以视图的代码设计角度来看,它并不完美:由于操作与Article
紧密耦合,不如将操作直接绑定到ArticleAdmin
对象上更有意义。
这样做十分简单:
class ArticleAdmin(admin.ModelAdmin):
...
actions = ['make_published']
def make_published(self, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
首先注意,我们将make_published
放到一个方法中,并重命名 modeladmin
参数为self
,其次,我们现在将'make_published'
字符串放进了actions
,而不是一个直接的函数引用。 这样会让 ModelAdmin
将这个操作视为方法。
将Action 定义为方法,可以使操作以更加直接、符合语言习惯的方式来访问ModelAdmin
,并可以调用Admin 提供的任何方法。
例如,我们可以使用self
向用户发送消息来告诉她Action 成功了:
class ArticleAdmin(admin.ModelAdmin):
...
def make_published(self, request, queryset):
rows_updated = queryset.update(status='p')
if rows_updated == 1:
message_bit = "1 story was"
else:
message_bit = "%s stories were" % rows_updated
self.message_user(request, "%s successfully marked as published." % message_bit)
这会使动作与后台在成功执行动作后做的事情相匹配:
默认情况下,在执行Actions 之后,用户会简单地通过重定向返回到之前的更改列表页面中。 然而,一些Action,尤其是更加复杂的操作,需要返回一个中间页面。 例如,内建的删除操作,在删除选中对象之前需要向用户询问来确认。
要提供中间页面,只要从你的操作返回HttpResponse
(或其子类)就可以了。 例如,你可以编写一个简单的导出函数,它使用Django 的serialization functions来将一些选中的对象转换为JSON:
from django.http import HttpResponse
from django.core import serializers
def export_as_json(modeladmin, request, queryset):
response = HttpResponse(content_type="application/json")
serializers.serialize("json", queryset, stream=response)
return response
一般情况下,上面的代码的实现方式并不是很好。 大多数情况下,最佳实践是返回 HttpResponseRedirect
,并且使用户重定向到你编写的视图中,向GET查询字符串传递选中对象的列表。
这需要你在中间界面上提供复杂的交互逻辑。 例如,如果你打算提供一个更加复杂的导出函数,你会希望让用户选择一种格式,以及可能在导出中包含一个含有字段的列表。 最佳方式是编写一个小型的操作,简单重定向到你的自定义导出视图中:
from django.contrib import admin
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect
def export_selected_objects(modeladmin, request, queryset):
selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
ct = ContentType.objects.get_for_model(queryset.model)
return HttpResponseRedirect("/export/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))
正如你所看到的,行动是简单的部分;所有复杂的逻辑将属于您的导出视图。 这需要处理任何类型的对象,所以需要处理ContentType
。
这个视图的编写作为一个练习留给读者。
AdminSite。
add_action
(action,name = None)[source] ¶如果一些操作对管理站点的任何对象都可用的话,是非常不错的 -- 上面所定义的导出操作是个不错的备选方案。 你可以使用AdminSite.add_action()
让一个操作在全局都可以使用。 像这样:
from django.contrib import admin
admin.site.add_action(export_selected_objects)
这样,export_selected_objects
操作可以在全局使用,名称为“export_selected_objects”。 你可以显式指定Action的名称 —— 如果你想以编程的方式remove the action 是非常有用的 —— 通过向AdminSite.add_action()
传递第二个参数:
admin.site.add_action(export_selected_objects, 'export_selected')
有时你需要禁用特定的操作 -- 尤其是registered site-wide -- 对于特定的对象。 你可以使用一些方法来禁用操作:
AdminSite。
disable_action
(name)[source]¶如果你需要禁用site-wide action ,你可以调用 AdminSite.disable_action()
。
例如,你可以使用这个方法来移除内建的“删除选中的对象”操作:
admin.site.disable_action('delete_selected')
一旦你执行了上面的代码,这个操作不再对整个站点中可用。
然而,如果你需要为特定的模型重新启动在全局禁用的对象,把它显式放在ModelAdmin.actions
列表中就可以了:
# 全站禁用删除功能
admin.site.disable_action('delete_selected')
# 这个ModelAdmin不会有删除功能
class SomeModelAdmin(admin.ModelAdmin):
actions = ['some_other_action']
...
# 这个声明还要用
class AnotherModelAdmin(admin.ModelAdmin):
actions = ['delete_selected', 'a_third_action']
...
ModelAdmin
¶的所有操作如果你想批量移除所提供 ModelAdmin
上的所有操作,可以把ModelAdmin.actions
设置为None
:
class MyModelAdmin(admin.ModelAdmin):
actions = None
这样会告诉ModelAdmin
,不要展示或者允许任何操作,包括site-wide actions。
的ModelAdmin。
get_actions
(request)[source]¶最后,你可以通过覆写ModelAdmin.get_actions()
,对每个请求(每个用户)按需开启或禁用操作。
这个函数返回包含允许操作的字典。 字典的键是操作的名称,值是 (function, name, short_description)
元组。
多数情况下,你会按需使用这一方法,来从超类中的列表移除操作。 例如,如果我只希望名称以'J'开头的用户可以批量删除对象,我可以执行下面的代码:
class MyModelAdmin(admin.ModelAdmin):
...
def get_actions(self, request):
actions = super(MyModelAdmin, self).get_actions(request)
if request.user.username[0].upper() != 'J':
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
2017年9月6日