Структура сообщений

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

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

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

Сообщения реализуются через класс middleware и соответствующий context processor.

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

  • 'django.contrib.messages' находится в INSTALLED_APPS.

  • MIDDLEWARE содержит 'django.contrib.sessions.middleware.SessionMiddleware' и 'django.contrib.messages.middleware.MessageMiddleware'.

    По умолчанию storage backend полагается на sessions. Поэтому 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 байт.

Changed in Django 3.2:

Формат сообщений был изменен на совместимый с RFC 6265 формат.

class storage.fallback.FallbackStorage

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

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

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 может быть использована для изменения минимального уровня записи (или это может быть changed per request). Попытки добавить сообщения с уровнем меньше этого будут проигнорированы.

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

Теги сообщения - это строковое представление уровня сообщения плюс любые дополнительные теги, которые были добавлены непосредственно в представлении (подробнее см. ниже Adding extra message tags). Теги хранятся в строке и разделяются пробелами. Обычно теги сообщений используются как классы 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 storage.base.Message

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

  • message: Фактический текст сообщения.
  • level: Целое число, описывающее тип сообщения (см. раздел message levels выше).
  • 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.

Примечание

Если вы создаете приложение многократного использования, рекомендуется использовать только встроенные message levels и не полагаться на какие-либо пользовательские уровни.

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

Минимальный уровень записи может быть установлен по запросу с помощью метода 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)

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

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

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

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

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

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

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

Настройки

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

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

Вернуться на верх