Spec-Zone.ru › Django 6.0

Фреймворк сообщений

В веб-приложениях часто требуется показывать пользователю одноразовое уведомление (также известное как «flash-сообщение») после обработки формы или других видов пользовательского ввода.

Для этого Django обеспечивает полную поддержку обмена сообщениями на основе файлов cookie и сессий как для анонимных, так и для аутентифицированных пользователей. Фреймворк сообщений позволяет временно сохранять сообщения в одном запросе и получать их для отображения в следующем запросе (обычно в ближайшем). Каждому сообщению присваивается определённый level, который определяет его приоритет (например, info, warning или error).

Включение сообщений

Сообщения реализованы с помощью класса промежуточного ПО и соответствующего процессора контекста.

Настройки по умолчанию settings.py, создаваемые django-admin startproject, уже содержат все параметры, необходимые для включения функциональности сообщений:

  • 'django.contrib.messages' находится в INSTALLED_APPS.
  • MIDDLEWARE содержит 'django.contrib.sessions.middleware.SessionMiddleware' и 'django.contrib.messages.middleware.MessageMiddleware'.

    Стандартный бэкенд хранилища использует сессии. Поэтому SessionMiddleware должен быть включён и располагаться перед MessageMiddleware в MIDDLEWARE.

  • Параметр 'context_processors' бэкенда DjangoTemplates, заданного в параметре TEMPLATES, содержит 'django.contrib.messages.context_processors.messages'.

Если вы не хотите использовать сообщения, можно удалить 'django.contrib.messages' из INSTALLED_APPS, строку MessageMiddleware из MIDDLEWARE и процессор контекста messages из TEMPLATES.

Настройка механизма сообщений

Бэкенды хранилища

Фреймворк сообщений может использовать разные бэкенды для хранения временных сообщений.

Django предоставляет три встроенных класса хранилища в django.contrib.messages:

class storage.session.SessionStorage

Этот класс хранит все сообщения в сессии запроса. Поэтому для его работы требуется приложение Django contrib.sessions.

class storage.cookie.CookieStorage

Этот класс сохраняет данные сообщений в файле cookie (подписанном секретным хешем для предотвращения подмены), чтобы уведомления сохранялись между запросами. Старые сообщения удаляются, если размер данных в файле cookie превысит 2048 байт.

class storage.fallback.FallbackStorage

Этот класс сначала использует CookieStorage, а для сообщений, которые не помещаются в один файл cookie, использует SessionStorage. Для его работы также требуется приложение Django contrib.sessions.

Такое поведение позволяет по возможности не записывать данные в сессию. В обычных случаях оно должно обеспечивать наилучшую производительность.

FallbackStorage — класс хранилища, используемый по умолчанию. Если он вам не подходит, можно выбрать другой класс хранилища, указав его полный путь импорта в MESSAGE_STORAGE, например:

MESSAGE_STORAGE = "django.contrib.messages.storage.cookie.CookieStorage"
class storage.base.BaseStorage

Чтобы написать собственный класс хранилища, создайте подкласс класса BaseStorage из django.contrib.messages.storage.base и реализуйте методы _get и _store.

Уровни сообщений

Фреймворк сообщений основан на настраиваемой архитектуре уровней, похожей на архитектуру модуля логирования Python. Уровни сообщений позволяют группировать сообщения по типам, чтобы их можно было фильтровать или по-разному отображать в представлениях и шаблонах.

Встроенные уровни, которые можно напрямую импортировать из django.contrib.messages:

Константа

Назначение

DEBUG

Сообщения, связанные с разработкой, которые игнорируются (или удаляются) при развёртывании в рабочей среде

INFO

Информационные сообщения для пользователя

SUCCESS

Действие выполнено успешно, например: «Ваш профиль успешно обновлён»

WARNING

Сбой не произошёл, но может произойти в ближайшее время

ERROR

Действие выполнено неуспешно или произошёл другой сбой

Параметр MESSAGE_LEVEL позволяет изменить минимальный регистрируемый уровень (также его можно изменять для каждого запроса). Попытки добавить сообщения с уровнем ниже этого значения будут игнорироваться.

Теги сообщений

Теги сообщений — это строковое представление уровня сообщения и любых дополнительных тегов, добавленных непосредственно в представлении (подробности см. ниже в разделе Добавление дополнительных тегов сообщений). Теги хранятся в виде строки и разделяются пробелами. Обычно теги сообщений используются как классы CSS, чтобы настраивать стиль сообщений в зависимости от их типа. По умолчанию у каждого уровня есть один тег, представляющий собой имя соответствующей константы в нижнем регистре:

Константа уровня

Тег

DEBUG

debug

INFO

info

SUCCESS

success

WARNING

warning

ERROR

error

Чтобы изменить теги по умолчанию для уровня сообщений (встроенного или пользовательского), задайте в параметре MESSAGE_TAGS словарь с уровнями, которые требуется изменить. Поскольку это расширяет набор тегов по умолчанию, достаточно указать теги только для уровней, которые вы хотите переопределить:

from django.contrib.messages import constants as messages

MESSAGE_TAGS = {
    messages.INFO: "",
    50: "critical",
}

Использование сообщений в представлениях и шаблонах

add_message(request, level, message, extra_tags='', fail_silently=False) [исходный код]

Добавление сообщения

Чтобы добавить сообщение, вызовите:

from django.contrib import messages

messages.add_message(request, messages.INFO, "Hello world.")

Некоторые вспомогательные методы предоставляют стандартный способ добавления сообщений с часто используемыми тегами (обычно представленными классами HTML для сообщения):

messages.debug(request, "%s SQL statements were executed." % count)
messages.info(request, "Three credits remain in your account.")
messages.success(request, "Profile details updated.")
messages.warning(request, "Your account expires in three days.")
messages.error(request, "Document deleted.")

Отображение сообщений

get_messages(request) [исходный код]

В шаблоне используйте, например:

{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
    {% endfor %}
</ul>
{% endif %}

Если вы используете процессор контекста, шаблон следует отрисовывать с помощью RequestContext. В противном случае убедитесь, что messages доступен в контексте шаблона.

Даже если вы знаете, что сообщение только одно, всё равно следует перебрать последовательность messages, иначе хранилище сообщений не будет очищено для следующего запроса.

Процессор контекста также предоставляет переменную DEFAULT_MESSAGE_LEVELS, представляющую собой соответствие имён уровней сообщений их числовым значениям:

{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
        {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
        {{ message }}
    </li>
    {% endfor %}
</ul>
{% endif %}

Вне шаблонов можно использовать get_messages():

from django.contrib.messages import get_messages

storage = get_messages(request)
for message in storage:
    do_something_with_the_message(message)

Например, можно получить все сообщения и вернуть их в JSONResponseMixin вместо TemplateResponseMixin.

get_messages() возвращает экземпляр настроенного бэкенда хранилища.

Класс Message

class Message [исходный код]

При переборе списка сообщений в шаблоне вы получаете экземпляры класса Message. У них всего несколько атрибутов:

  • message: собственно текст сообщения.
  • level: целое число, описывающее тип сообщения (см. раздел Уровни сообщений выше).
  • tags: строка, объединяющая все теги сообщения (extra_tags и level_tag), разделённые пробелами.
  • extra_tags: строка с пользовательскими тегами для этого сообщения, разделёнными пробелами. По умолчанию она пуста.
  • level_tag: строковое представление уровня. По умолчанию это имя соответствующей константы в нижнем регистре, но при необходимости его можно изменить с помощью параметра MESSAGE_TAGS.

Создание пользовательских уровней сообщений

Уровни сообщений — это обычные целые числа, поэтому можно определить собственные константы уровней и использовать их для более точной настройки обратной связи для пользователя, например:

CRITICAL = 50


def my_view(request):
    messages.add_message(request, CRITICAL, "A serious error occurred.")

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

Константа уровня

Значение

DEBUG

10

INFO

20

SUCCESS

25

WARNING

30

ERROR

40

Если вам нужно идентифицировать пользовательские уровни в HTML или CSS, задайте соответствие с помощью параметра MESSAGE_TAGS.

Примечание

При создании повторно используемого приложения рекомендуется использовать только встроенные уровни сообщений и не полагаться на пользовательские уровни.

Изменение минимального регистрируемого уровня для каждого запроса

Минимальный регистрируемый уровень можно задать отдельно для каждого запроса с помощью метода set_level:

from django.contrib import messages

# Change the messages level to ensure the debug message is added.
messages.set_level(request, messages.DEBUG)
messages.debug(request, "Test message...")

# In another request, record only messages with a level of WARNING and higher
messages.set_level(request, messages.WARNING)
messages.success(request, "Your profile was updated.")  # ignored
messages.warning(request, "Your account is about to expire.")  # recorded

# Set the messages level back to default.
messages.set_level(request, None)

Аналогичным образом текущий эффективный уровень можно получить с помощью get_level:

from django.contrib import messages

current_level = messages.get_level(request)

Дополнительные сведения о работе минимального регистрируемого уровня см. выше в разделе Уровни сообщений.

Добавление дополнительных тегов сообщений

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

messages.add_message(request, messages.INFO, "Over 9000!", extra_tags="dragonball")
messages.error(request, "Email box full", extra_tags="email")

Дополнительные теги добавляются перед тегом по умолчанию для данного уровня и разделяются пробелами.

Игнорирование ошибок при отключённом фреймворке сообщений

Если вы разрабатываете повторно используемое приложение (или другой программный компонент) с функциональностью обмена сообщениями, но не хотите требовать от пользователей её включения, можно передать дополнительный именованный аргумент fail_silently=True любому из методов семейства add_message. Например:

messages.add_message(
    request,
    messages.SUCCESS,
    "Profile details updated.",
    fail_silently=True,
)
messages.info(request, "Hello world.", fail_silently=True)

Примечание

Параметр fail_silently=True скрывает только MessageFailure, которая иначе возникла бы, если бы фреймворк сообщений был отключён, а кто-либо попытался использовать один из методов семейства add_message. Он не скрывает ошибки, которые могут возникнуть по другим причинам.

Добавление сообщений в представлениях на основе классов

class views.SuccessMessageMixin

Добавляет атрибут сообщения об успешном выполнении в классы на основе FormView

get_success_message(cleaned_data)

cleaned_data — это очищенные данные формы, используемые для форматирования строки

Пример views.py:

from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author


class AuthorCreateView(SuccessMessageMixin, CreateView):
    model = Author
    success_url = "/success/"
    success_message = "%(name)s was created successfully"

Очищенные данные из form доступны для подстановки в строку с помощью синтаксиса %(field_name)s. Если для ModelForms нужен доступ к полям сохранённого object, переопределите метод get_success_message().

Пример views.py для ModelForms:

from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import ComplicatedModel


class ComplicatedCreateView(SuccessMessageMixin, CreateView):
    model = ComplicatedModel
    success_url = "/success/"
    success_message = "%(calculated_field)s was created successfully"

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(
            cleaned_data,
            calculated_field=self.object.calculated_field,
        )

Срок действия сообщений

Сообщения помечаются для удаления при переборе экземпляра хранилища (и удаляются при обработке ответа).

Чтобы сообщения не удалялись, после перебора задайте для хранилища сообщений значение False:

storage = messages.get_messages(request)
for message in storage:
    do_something_with(message)
storage.used = False

Поведение при параллельных запросах

Из-за особенностей работы файлов cookie (и, следовательно, сессий) поведение любых бэкендов, использующих файлы cookie или сессии, не определено, если один и тот же клиент параллельно отправляет несколько запросов, устанавливающих или получающих сообщения. Например, если клиент инициирует запрос, создающий сообщение в одном окне (или вкладке), а затем в другом окне отправляет запрос, получающий сообщения, которые ещё не перебирались, до перенаправления первого окна сообщение может появиться во втором окне вместо первого, где его ожидали.

Иными словами, при нескольких одновременных запросах от одного клиента не гарантируется, что сообщения будут доставлены в то же окно, в котором они были созданы, а в некоторых случаях они могут не доставиться вовсе. Обычно это не является проблемой для большинства приложений; в HTML5 она исчезнет, поскольку у каждого окна или вкладки будет собственный контекст просмотра.

Параметры

На поведение сообщений влияют несколько параметров:

  • MESSAGE_LEVEL
  • MESSAGE_STORAGE
  • MESSAGE_TAGS

Для бэкендов, использующих файлы cookie, параметры cookie берутся из настроек cookie сессии:

  • SESSION_COOKIE_DOMAIN
  • SESSION_COOKIE_SECURE
  • SESSION_COOKIE_HTTPONLY

Тестирование

Этот модуль предоставляет специальный метод проверки для тестирования сообщений, прикреплённых к объекту HttpResponse.

Чтобы использовать эту проверку, добавьте MessagesTestMixin в иерархию классов:

from django.contrib.messages.test import MessagesTestMixin
from django.test import TestCase


class MsgTestCase(MessagesTestMixin, TestCase):
    pass

Затем в тестах наследуйтесь от MsgTestCase.

MessagesTestMixin.assertMessages(response, expected_messages, ordered=True) [исходный код]

Проверяет, что messages, добавленные в response, соответствуют expected_messages.

expected_messages — это список объектов Message.

По умолчанию сравнение зависит от порядка элементов. Это поведение можно отключить, задав аргументу ordered значение False.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/ref/contrib/messages/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API