Действия администратора ¶
В двух словах, основной рабочий процесс администратора Django - «выбрать объект, а затем изменить его». Это хорошо работает для большинства случаев использования. Однако, если вам нужно внести одно и то же изменение во многие объекты одновременно, этот рабочий процесс может быть довольно утомительным.
В этих случаях администратор Django позволяет вам писать и регистрировать «действия» - функции, которые вызываются со списком объектов, выбранных на странице списка изменений.
Если вы посмотрите любой список изменений в админке, вы увидите эту функцию в действии; Django поставляется с действием «удалить выбранные объекты», доступным для всех моделей. Например, вот пользовательский модуль из встроенного django.contrib.auth
приложения Django
:
Предупреждение
Действие «удалить выбранные объекты» используется QuerySet.delete()
из соображений эффективности, что имеет важное предостережение: delete()
метод вашей модели не будет вызываться.
Если вы хотите переопределить это поведение, вы можете переопределить
ModelAdmin.delete_queryset()
или написать настраиваемое действие, которое выполняет удаление в соответствии с вашим предпочтением - например, путем вызова
Model.delete()
каждого из выбранных элементов.
Дополнительные сведения о массовом удалении см. В документации по удалению объектов .
Читайте дальше, чтобы узнать, как добавить свои собственные действия в этот список.
Написание действий ¶
Самый простой способ объяснить действия - это на примере, так что давайте погрузимся в него.
Распространенным вариантом использования действий администратора является массовое обновление модели. Представьте себе новостное приложение с 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
Обычная задача, которую мы можем выполнить с такой моделью, - это обновить статус статьи с «черновика» до «опубликована». Мы могли бы легко сделать это в админке, по одной статье за раз, но если бы мы хотели опубликовать группу статей, это было бы утомительно. Итак, давайте напишем действие, которое позволит нам изменить статус статьи на «опубликована».
Написание функций действий ¶
Во-первых, нам нужно написать функцию, которая будет вызываться, когда действие запускается администратором. Функции действий - это обычные функции, которые принимают три аргумента:
- Электрический ток
ModelAdmin
HttpRequest
, Представляющий текущий запрос,- Объект,
QuerySet
содержащий набор объектов, выбранных пользователем.
Наша функция публикации этих статей не будет нуждаться ModelAdmin
в объекте запроса или, но мы будем использовать набор запросов:
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
Примечание
Для лучшей производительности мы используем метод обновления набора запросов . Другие типы действий могут потребоваться для работы с каждым объектом индивидуально; в этих случаях мы перебираем набор запросов:
for obj in queryset:
do_something_with(obj)
На самом деле это все, что нужно для написания экшена! Однако мы сделаем еще один необязательный, но полезный шаг и дадим действию «приятный» заголовок в админке. По умолчанию это действие отображается в списке действий как «Сделать опубликованным» - имя функции с заменой подчеркиваний на пробелы. Это нормально, но мы можем предоставить лучшее, более понятное для человека имя, используя
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()
декоратором, чтобы предоставить удобочитаемые описания для зарегистрированных там функций обратного вызова.
description
Аргумент action()
декоратора эквивалентен установка short_description
атрибута функции действия непосредственно в предыдущих версиях. Прямая установка атрибута по-прежнему поддерживается для обратной совместимости.
Добавление действий в ¶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 для вывода некоторых выбранных объектов в виде 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
.
Написание этой точки зрения оставлено читателю в качестве упражнения.
Делаем действия доступными для всего сайта ¶
-
AdminSite.
add_action
( действие , имя = Нет ) ¶ Некоторые действия лучше, если они доступны для любого объекта на сайте администратора - действие экспорта, определенное выше, было бы хорошим кандидатом. Вы можете сделать действие глобально доступным, используя
AdminSite.add_action()
. Например:from django.contrib import admin admin.site.add_action(export_selected_objects)
Это делает
export_selected_objects
действие глобально доступным как действие с именем «export_selected_objects». Вы можете явно дать действию имя - хорошо, если позже вы захотите программно удалить действие, - передав второй аргумент вAdminSite.add_action()
:admin.site.add_action(export_selected_objects, 'export_selected')
Отключение действий ¶
Иногда вам нужно отключить определенные действия - особенно те, которые зарегистрированы на всем сайте - для определенных объектов. Есть несколько способов отключить действия:
Отключение действия на уровне сайта ¶
-
AdminSite.
disable_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
Если вы хотите не сыпучие действий , доступных для данного ModelAdmin
, установить
ModelAdmin.actions
на None
:
class MyModelAdmin(admin.ModelAdmin):
actions = None
Это указывает, что ModelAdmin
не следует отображать и не разрешать какие-либо действия, в том числе действия на уровне сайта .
Условное включение или отключение действий ¶
-
ModelAdmin.
get_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
и соответствующие проверки методов:
'add'
:ModelAdmin.has_add_permission()
'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))
permissions
Аргумент action()
декоратора эквивалентен установка allowed_permissions
атрибута функции действия непосредственно в предыдущих версиях. Прямая установка атрибута по-прежнему поддерживается для обратной совместимости.
action
Декоратор ¶
-
action
( * , разрешения = Нет , описание = Нет ) ¶ - Новое в Django 3.2.
Этот декоратор можно использовать для установки определенных атрибутов в функциях настраиваемых действий, которые можно использовать с
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)
В этом случае он не будет добавлять атрибутов к функции.