Действия администратора
Основной рабочий процесс административной панели Django, по сути, сводится к «выбору объекта и его изменению». Это хорошо работает для большинства случаев использования. Однако, если вам нужно внести одно и то же изменение во множество объектов сразу, этот рабочий процесс может быть довольно утомительным.
В таких случаях административная панель Django позволяет записывать и регистрировать «действия» — простые функции, которые вызываются со списком выбранных объектов на странице изменения.
Если вы посмотрите на любую страницу изменения в административной панели, вы увидите это в действии; Django поставляется с действием «удалить выбранные объекты», доступным для всех моделей. Например, вот модуль пользователя из встроенного приложения Django django.contrib.auth:
Предупреждение
Действие «удалить выбранные объекты» использует QuerySet.delete() по причинам эффективности, что имеет важное замечание: метод delete() вашей модели не будет вызван.
Если вы хотите переопределить это поведение, просто напишите пользовательское действие, которое выполнит удаление по вашему желанию — например, вызвав 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): # __unicode__ on Python 2
return self.title
Типичной задачей, которую мы можем выполнить с такой моделью, является обновление статуса статьи из «черновика» в «опубликованную». Мы могли бы легко сделать это в административной панели по одной статье за раз, но если мы захотим массово опубликовать группу статей, это будет утомительно. Итак, давайте напишем действие, которое позволит изменить статус статьи на «опубликованную».
Написание функций действий
Во-первых, нам нужно написать функцию, которая вызывается при запуске действия из администратора. Функции действий — это обычные функции, которые принимают три аргумента:
- Текущий
ModelAdmin - Объект
HttpRequest, представляющий текущий запрос, - Объект
QuerySet, содержащий набор объектов, выбранных пользователем.
Наша функция publish-these-articles не будет нуждаться в объекте ModelAdmin или объекте запроса, но мы будем использовать набор объектов:
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
Примечание
Для наилучшей производительности мы используем метод обновления набора объектов. Другим типам действий может потребоваться работать с каждым объектом по отдельности; в этих случаях мы просто перебирали бы набор объектов:
for obj in queryset:
do_something_with(obj)
Это, на самом деле, все, что нужно для написания действия! Однако мы сделаем еще один необязательный, но полезный шаг и дадим действию «красивое» имя в административной панели. По умолчанию это действие будет отображаться в списке действий как «Опубликовать» — имя функции со знаками подчеркивания, замененными пробелами. Это нормально, но мы можем предоставить более понятное для пользователя имя, присвоив функции 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
Далее нам нужно сообщить нашему объекту ModelAdmin об этом действии. Это работает так же, как любая другая опция конфигурации. Таким образом, полная запись 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)
Этот код даст нам страницу изменения администратора, которая будет выглядеть примерно так:
Это все, что нужно! Если вы хотите написать свои собственные действия, теперь у вас достаточно знаний для начала. Остальная часть этого документа просто рассматривает более продвинутые методы.
Обработка ошибок в действиях
Если есть предсказуемые условия возникновения ошибок во время выполнения вашего действия, вы должны вежливо сообщить пользователю о проблеме. Это означает обработку исключений и использование 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 искать действие в виде метода.
Определение действий как методов предоставляет действию более прямой идиоматический доступ к самому объекту ModelAdmin, позволяя действию вызывать любые методы, предоставляемые администратором.
Например, мы можем использовать self для отображения сообщения пользователю, сообщая ей об успешном выполнении действия:
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)
Это делает действие аналогичным тому, что делает сам администратор после успешного выполнения действия:
Действия, предоставляющие промежуточные страницы
По умолчанию после выполнения действия пользователь просто перенаправляется обратно на исходную страницу списка изменений. Однако некоторые действия, особенно более сложные, потребуют возврата промежуточных страниц. Например, встроенное действие удаления запрашивает подтверждение перед удалением выбранных объектов.
Чтобы предоставить промежуточную страницу, просто верните объект HttpResponse (или подкласс) из вашего действия. Например, вы можете написать простую функцию экспорта, которая использует функции сериализации Django для выгрузки некоторых выбранных объектов в формате 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». Вы можете явно указать имя действия — это полезно, если вы хотите впоследствии программно удалить действие — передав второй аргумент вAdminSite.add_action():admin.site.add_action(export_selected_objects, 'export_selected')
Отключение действий
Иногда вам нужно отключить определенные действия — особенно те, которые зарегистрированы на уровне всего сайта — для конкретных объектов. Есть несколько способов отключить действия:
Отключение действия для всего сайта
-
AdminSite.disable_action(name)[source] -
Если необходимо отключить действие для всего сайта, можно вызвать
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(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
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/ref/contrib/admin/actions/