Фреймворк сообщений
В веб-приложениях часто требуется показывать пользователю одноразовое уведомление (также известное как «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. Для его работы также требуется приложение Djangocontrib.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:
Константа | Назначение |
|---|---|
| Сообщения, связанные с разработкой, которые игнорируются (или удаляются) при развёртывании в рабочей среде |
| Информационные сообщения для пользователя |
| Действие выполнено успешно, например: «Ваш профиль успешно обновлён» |
| Сбой не произошёл, но может произойти в ближайшее время |
| Действие выполнено неуспешно или произошёл другой сбой |
Параметр MESSAGE_LEVEL позволяет изменить минимальный регистрируемый уровень (также его можно изменять для каждого запроса). Попытки добавить сообщения с уровнем ниже этого значения будут игнорироваться.
Использование сообщений в представлениях и шаблонах
-
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.")
Создавая пользовательские уровни сообщений, следует избегать конфликтов с существующими уровнями. Значения встроенных уровней:
Константа уровня | Значение |
|---|---|
| 10 |
| 20 |
| 25 |
| 30 |
| 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)
Дополнительные сведения о работе минимального регистрируемого уровня см. выше в разделе Уровни сообщений.
Игнорирование ошибок при отключённом фреймворке сообщений
Если вы разрабатываете повторно используемое приложение (или другой программный компонент) с функциональностью обмена сообщениями, но не хотите требовать от пользователей её включения, можно передать дополнительный именованный аргумент 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 она исчезнет, поскольку у каждого окна или вкладки будет собственный контекст просмотра.
Параметры
На поведение сообщений влияют несколько параметров:
Для бэкендов, использующих файлы cookie, параметры cookie берутся из настроек cookie сессии:
Тестирование
Этот модуль предоставляет специальный метод проверки для тестирования сообщений, прикреплённых к объекту 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/