Действия администрации

Вкратце основной способ работы в администрировании Django: «выберите объект, затем измените его». Он хорошо работает в большинстве случаев использования. Однако, если вам нужно внести одно и то же изменение в несколько объектов одновременно, этот способ работы может быть довольно утомительным.

В этих случаях администрация Django позволяет вам писать и регистрировать «действия» - функции, вызываемые со списком объектов, выбранных на странице списка для модификации.

Если вы посмотрите на любой список объектов для редактирования в админке, вы увидите эту функцию в действии; В Django есть действие «удалить выбранные объекты», доступное для всех моделей. Например, вот пользовательский модуль для приложения, django.contrib.auth встроенного в Django:

../../../../_images/admin-actions.png

Предупреждение

Действие «удалить выбранные объекты» используется 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)

На самом деле это все, что нужно написать для действия! Однако мы собираемся предпринять необязательный, но полезный дополнительный шаг и присвоить действию «красивый» заголовок в интерфейсе администратора. По умолчанию это действие отображается в списке действий как «Опубликовать» - имя функции с подчеркиванием заменено пробелами. Это нормально, но мы можем предоставить более удобное и удобное имя, присвоив функции 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)

С помощью этого кода список объектов, которые нужно изменить в интерфейсе администрирования, выглядит следующим образом:

../../../../_images/adding-actions-to-the-modeladmin.png

Вот и все, что нужно сделать! Если вы умираете от желания написать свои собственные действия, теперь вы знаете достаточно, чтобы начать. Остальная часть этого документа охватывает более сложные методы.

Обработка ошибок в действиях

Если есть какие-либо предсказуемые условия ошибки, которые могут возникнуть при выполнении вашего действия, вы должны исправить это, уведомив пользователя о проблеме. Это означает обработку исключений и использование 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 для отображения сообщения пользователю, информирующего его об успешном выполнении действия:

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)

Это делает действие похожим на то, что делает сама администрация после успешного выполнения действия:

../../../../_images/actions-as-modeladmin-methods.png

Действия, которые предоставляют промежуточные страницы

По умолчанию после выполнения действия пользователь перенаправляется на исходную страницу списка редактирования. Однако некоторые действия, особенно более сложные, могут потребовать ссылки на промежуточные страницы. Например, встроенное действие удаления запрашивает подтверждение перед удалением выбранных объектов.

Чтобы предоставить промежуточную страницу, верните ответ 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() .

Это возвращает словарь разрешенных действий. Ключи - это имена действий, а значения - это кортежи .(fonction, nom, description courte)

Например, если вы хотите, чтобы только пользователи, чьи фамилии начинаются с "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

Настройка разрешений для действий

Действия могут ограничить их доступность для пользователей с определенными разрешениями, установив атрибут allowed_permissions в функции действия.

def make_published(modeladmin, request, queryset):
    queryset.update(status='p')
make_published.allowed_permissions = ('change',)

Действие make_published() будет доступно только тем пользователям, которые передали управление ModelAdmin.has_change_permission() .

Если allowed_permissions у вас более одного разрешения, действие будет доступно, если пользователь передаст хотя бы один из элементов управления.

Доступные значения allowed_permissions и соответствующие им методы контроля:

Вы можете указать другие значения, если вы реализуете соответствующий метод для соответствующего класса .has_<valeur>_permission(self, request) ModelAdmin

Например :

from django.contrib import admin
from django.contrib.auth import get_permission_codename

class ArticleAdmin(admin.ModelAdmin):
    actions = ['make_published']

    def make_published(self, request, queryset):
        queryset.update(status='p')
    make_published.allowed_permissions = ('publish',)

    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))

Copyright ©2020 All rights reserved