Структура сообщений¶
Довольно часто в веб-приложениях требуется отобразить одноразовое уведомление (также известное как «флэш-сообщение») для пользователя после обработки формы или других типов пользовательского ввода.
Для этого 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, где каждое окно/вкладка будет иметь свой собственный контекст просмотра.