Действия администратора
Основной рабочий процесс административной панели 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, содержащий набор объектов, выбранных пользователем.
Нашей функции «опубликовать эти статьи» не понадобятся 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.11/ref/contrib/admin/actions/