简而言之,Django管理员的基本工作流程是“选择一个对象,然后更改它。”这在大多数用例中都很有效。但是,如果您需要同时对许多对象进行相同的更改,则此工作流可能非常繁琐。
在这些情况下,Django的admin允许您编写和注册“actions”--调用这些函数的对象列表是在更改列表页面上选择的。
如果您查看管理员中的任何更改列表,您将看到此功能正在运行;Django附带了一个对所有模型都可用的“删除选定对象”操作。例如,下面是Django内置的用户模块 django.contrib.auth
应用程序:
警告
“删除选定对象”操作使用 QuerySet.delete()
出于效率的考虑,这有一个重要的警告:您的模型 delete()
将不调用方法。
如果要重写此行为,可以重写 ModelAdmin.delete_queryset()
或者编写一个以您喜欢的方式进行删除的自定义操作——例如,通过调用 Model.delete()
对于每个选定的项目。
有关批量删除的更多背景信息,请参阅 object deletion .
继续阅读,了解如何将自己的操作添加到此列表中。
解释行动的最简单方法是通过例子,所以让我们深入了解一下。
管理操作的一个常见用例是模型的批量更新。设想一个新闻应用程序 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):
return self.title
对于这样的模型,我们可能执行的一个常见任务是将文章的状态从“草稿”更新为“已发布”。我们可以很容易地在管理员中一次只发布一篇文章,但是如果我们想批量发布一组文章,那将是非常乏味的。那么,让我们编写一个操作,让我们将文章的状态更改为“已发布”。
首先,我们需要编写一个函数,该函数在管理员触发操作时被调用。Action函数是常规函数,有三个参数:
安 HttpRequest
表示当前请求,
A QuerySet
包含用户选择的对象集。
我们的“发布这些文章”功能不需要 ModelAdmin
或者请求对象,但我们将使用queryset::
def make_published(modeladmin, request, queryset):
queryset.update(status="p")
备注
为了获得最佳性能,我们使用的是 update method . 其他类型的操作可能需要单独处理每个对象;在这些情况下,我们将遍历queryset::
for obj in queryset:
do_something_with(obj)
实际上,这就是编写动作的全部内容!但是,我们将采取另一个可选但有用的步骤,并在管理员中为该操作指定一个“不错”的标题。默认情况下,此操作在操作列表中显示为“Make Publisher”--函数名称,下划线由空格替换。这很好,但是我们可以提供一个更好、更人性化的名称,方法是使用 action()
室内装饰师在 make_published
功能::
from django.contrib import admin
...
@admin.action(description="Mark selected stories as published")
def make_published(modeladmin, request, queryset):
queryset.update(status="p")
备注
这可能看起来很眼熟;管理员的 list_display
选项使用与 display()
修饰器为在那里注册的回调函数提供人类可读的描述。
ModelAdmin
¶接下来,我们需要通知 ModelAdmin
行动的这和其他配置选项一样工作。所以,完成 admin.py
操作及其注册如下:
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()
在响应中显示问题的用户友好描述。
您可以利用一些额外的选项和可能性来获得更高级的选项。
ModelAdmin
方法¶上面的示例显示 make_published
定义为函数的操作。这很好,但从代码设计的角度来看并不完美:因为动作与 Article
对象,将操作挂接到 ArticleAdmin
对象本身。
你可以这样做:
class ArticleAdmin(admin.ModelAdmin):
...
actions = ["make_published"]
@admin.action(description="Mark selected stories as published")
def make_published(self, request, queryset):
queryset.update(status="p")
首先注意我们已经搬家了 make_published
并将其重命名为 modeladmin
参数到 self
第二,我们现在把字符串 'make_published'
在里面 actions
而不是直接的函数引用。这告诉了 ModelAdmin
将操作作为一种方法进行查找。
将动作定义为方法可以让动作更习惯地访问 ModelAdmin
本身,允许操作调用管理员提供的任何方法。
例如,我们可以使用 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,
)
这将使操作与成功执行操作后管理员本身的操作相匹配:
默认情况下,执行操作后,用户将被重定向回原始更改列表页面。但是,有些操作,尤其是更复杂的操作,需要返回中间页。例如,内置的删除操作要求在删除选定对象之前进行确认。
若要提供中间页,请返回 HttpResponse
(或子类)来自您的操作。例如,您可以编写一个使用Django的导出函数 serialization functions 要将某些选定对象转储为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查询字符串中传递选定对象的列表。这允许您在中间页面上提供复杂的交互逻辑。例如,如果您想提供一个更完整的导出功能,您需要让用户选择一种格式,以及可能要包含在导出中的字段列表。最好的做法是编写一个小动作,重定向到您的自定义导出视图:
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect
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),
)
)
如您所见,操作相当短;所有复杂的逻辑都属于您的导出视图。这将需要处理任何类型的对象,因此 ContentType
.
写这个视图留给读者作为练习。
一些行动是最好的,如果他们可以 any 管理站点中的对象——上面定义的导出操作将是一个很好的候选者。您可以使用 AdminSite.add_action()
. 例如::
from django.contrib import admin
admin.site.add_action(export_selected_objects)
这使得 export_selected_objects
作为名为“导出选定对象”的操作全局可用的操作。您可以显式地给这个动作起一个名字——如果以后您想通过编程的方式实现,那就好了。 remove the action --通过将第二个参数传递给 AdminSite.add_action()
::
admin.site.add_action(export_selected_objects, "export_selected")
有时你需要禁用某些动作——尤其是那些 registered site-wide --对于特定对象。有几种方法可以禁用操作:
如果需要禁用 site-wide action 你可以调用 AdminSite.disable_action()
.
例如,可以使用此方法删除内置的“删除选定对象”操作:
admin.site.disable_action("delete_selected")
完成以上操作后,该操作将不再在站点范围内可用。
但是,如果需要为某个特定模型重新启用全局禁用的操作,请将其明确列在您的 ModelAdmin.actions
名单::
# Globally disable delete selected
admin.site.disable_action("delete_selected")
# This ModelAdmin will not have delete_selected available
class SomeModelAdmin(admin.ModelAdmin):
actions = ["some_other_action"]
...
# This one will
class AnotherModelAdmin(admin.ModelAdmin):
actions = ["delete_selected", "a_third_action"]
...
ModelAdmin
¶如果你想要 no 对于给定的 ModelAdmin
,集合 ModelAdmin.actions
到 None
::
class MyModelAdmin(admin.ModelAdmin):
actions = None
这告诉了 ModelAdmin
不显示或允许任何操作,包括 site-wide actions .
最后,您可以通过重写来有条件地启用或禁用每个请求上的操作(因此也可以基于每个用户)。 ModelAdmin.get_actions()
.
这将返回允许的操作字典。键是操作名,值是 (function, name, short_description)
元组。
例如,如果只希望名称以“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()
装饰器,并将 permissions
参数::
@admin.action(permissions=["change"])
def make_published(modeladmin, request, queryset):
queryset.update(status="p")
这个 make_published()
只有通过 ModelAdmin.has_change_permission()
检查。
如果 permissions
具有多个权限,则只要用户通过至少一项检查,操作就可用。
的可用值 permissions
相应的方法检查为:
'change'
: ModelAdmin.has_change_permission()
'delete'
: ModelAdmin.has_delete_permission()
'view'
: ModelAdmin.has_view_permission()
只要实现相应的 has_<value>_permission(self, request)
方法在 ModelAdmin
.
例如::
from django.contrib import admin
from django.contrib.auth import get_permission_codename
class ArticleAdmin(admin.ModelAdmin):
actions = ["make_published"]
@admin.action(permissions=["publish"])
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))
action
装饰师¶此修饰符可用于设置可与一起使用的自定义操作函数的特定属性 actions
::
@admin.action(
permissions=["publish"],
description="Mark selected stories as published",
)
def make_published(self, request, queryset):
queryset.update(status="p")
这等效于直接在函数上设置某些属性(具有原始的较长名称)::
def make_published(self, request, queryset):
queryset.update(status="p")
make_published.allowed_permissions = ["publish"]
make_published.short_description = "Mark selected stories as published"
使用此修饰符并不是创建操作函数所必需的,但在源代码中使用它(不带参数)作为标识函数用途的标记可能会很有用:
@admin.action
def make_inactive(self, request, queryset):
queryset.update(is_active=False)
在这种情况下,它不会向函数添加任何属性。
操作描述是%格式的,可能包含 '%(verbose_name)s'
和 '%(verbose_name_plural)s'
占位符,它们分别被模型的 verbose_name
和 verbose_name_plural
。
12月 18, 2023