消息框架

在 Web 应用程序中,通常需要在处理表单或其他类型的用户输入后向用户显示一次性通知消息(也称为“闪存消息”)。

为此,Django 为匿名用户和经过身份验证的用户提供对基于 cookie 和会话的消息传递的全面支持。消息框架允许您暂时将消息存储在一个请求中,并在后续请求(通常是下一个请求)中检索它们以进行显示。每条消息都标记有特定的 level,该标记确定其优先级(例如,infowarningerror)。

启用消息

消息是通过 中间件 类和相应的 上下文处理器 实现的。

django-admin startproject 创建的默认 settings.py 已包含启用消息功能所需的所有设置

  • 'django.contrib.messages'INSTALLED_APPS 中。

  • MIDDLEWARE 包含 'django.contrib.sessions.middleware.SessionMiddleware''django.contrib.messages.middleware.MessageMiddleware'

    默认的 存储后端 依赖于 会话。这就是为什么 SessionMiddleware 必须启用并出现在 MIDDLEWARE 中的 MessageMiddleware 之前的原因。

  • TEMPLATES 设置中定义的 DjangoTemplates 后端的 'context_processors' 选项包含 'django.contrib.messages.context_processors.messages'

如果您不想使用消息,可以从 INSTALLED_APPS 中删除 'django.contrib.messages',从 MIDDLEWARE 中删除 MessageMiddleware 行,以及从 TEMPLATES 中删除 messages 上下文处理器。

配置消息引擎

存储后端

消息框架可以使用不同的后端来存储临时消息。

Django 在 django.contrib.messages 中提供了三个内置存储类

class storage.session.SessionStorage

此类将所有消息存储在请求的会话中。因此,它需要 Django 的 contrib.sessions 应用程序。

class storage.cookie.CookieStorage

此类将消息数据存储在 cookie 中(使用秘密哈希进行签名以防止篡改),以便在请求之间保留通知。如果 cookie 数据大小超过 2048 字节,则会删除旧消息。

class storage.fallback.FallbackStorage

此类首先使用 CookieStorage,然后回退到使用 SessionStorage 来处理无法放入单个 cookie 中的消息。它还需要 Django 的 contrib.sessions 应用程序。

此行为避免尽可能地写入会话。在一般情况下,它应该提供最佳性能。

FallbackStorage 是默认存储类。如果不适合你的需求,你可以通过将 MESSAGE_STORAGE 设置为其完整导入路径来选择另一个存储类,例如

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

要编写自己的存储类,请在 django.contrib.messages.storage.base 中对 BaseStorage 类进行子类化,并实现 _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)[source]

添加消息

要添加消息,请调用

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)[source]

在模板中,使用类似

{% 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

消息[源代码]

当您在模板中循环遍历消息列表时,您得到的是消息类的实例。它们只有几个属性

  • 消息:消息的实际文本。
  • 级别:描述消息类型的整数(请参见上面的消息级别部分)。
  • 标签:一个字符串,组合了用空格分隔的所有消息标签(额外标签级别标签)。
  • 额外标签:一个字符串,包含此消息的自定义标签,用空格分隔。它默认情况下为空。
  • 级别标签:级别的字符串表示形式。默认情况下,它是关联常量的名称的小写版本,但如果您需要,可以使用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")

额外标记添加到该级别的默认标记之前,并用空格分隔。

当消息框架被禁用时静默失败

如果你正在编写一个可重用应用程序(或其他代码片段),并且想要包含消息传递功能,但不想要求你的用户在不想使用时启用它,你可以向 add_message 系列方法中的任何一个传递一个额外的关键字参数 fail_silently=True。例如

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

注意

设置 fail_silently=True 仅隐藏当消息框架被禁用并且尝试使用 add_message 系列方法之一时才会发生的 MessageFailure。它不会隐藏可能由于其他原因而发生的故障。

在基于类的视图中添加消息

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() 方法。

ModelForm 的 views.py 示例:

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 设置

测试

Django 5.0 中的新增功能。

此模块提供了一种定制的测试断言方法,用于测试附加到 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)[source]

断言 messages 添加到 response 匹配 expected_messages

expected_messagesMessage 对象的列表。

默认情况下,比较是依赖于排序的。你可以通过将 ordered 参数设置为 False 来禁用此功能。

返回顶部