django:消息框架源码分析与简单应用

一,消息框架The messages framework

在Web应用程序中,需要在处理表单或某些其他类型的用户输入后向用户显示一次性通知消息(也称为“即时消息”),通常很常见。
为此,Django为匿名用户和经过身份验证的用户提供了基于cookie和session的消息传递的全面支持。 消息框架可以将消息临时存储在一个请求中,并检索它们以在后续请求(通常是下一个请求)中显示。 每条消息都标记有确定其优先级的特定级别。
这个消息框架可供直接调用,允许设置功能引擎、消息类型与消息内容。

二,源码解读

1,启用消息Enabling messages

消息是通过中间件类和相应的上下文处理器实现的,具体来说就是由一下及部分组成:

  • MIDDLEWARE中的SessionMiddlewareMessageMiddleware
  • INSTALLED_APPS中的django.contrib.sessionsdjango.contrib.messages
  • TEMPLATES中的django.contrib.messages.context_processors.messages

而且都是在项目创建之初就默认配置好了的。

默认的存储后端依赖于session。 这就是为什么必须启用SessionMiddleware并使其出现在MIDDLEWARE中的​​MessageMiddleware之前的原因,这从源码可以了解到:

django\contrib\sessions\middleware.py:
class SessionMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
        self.get_response = get_response
        engine = import_module(settings.SESSION_ENGINE)
        self.SessionStore = engine.SessionStore

global_settings:
# The module to store session data
SESSION_ENGINE = 'django.contrib.sessions.backends.db'

不使用时,移除以上组件就行。

2,配置消息引擎Configuring the message engine

在这里插入图片描述

1,存储后台Storage backends

消息框架可以使用不同的后端来存储临时消息。
Django在django.contrib.messages提供了以下几个内置存储类:

  • class storage.session.SessionStorage:此d类将所有消息存储在请求的session中,依赖于django.contrib.sessions
  • class storage.cookie.CookieStorage:此类将消息数据存储在cookie中(使用秘密哈希值签名以防止操纵)以在请求中持久保存通知。 如果cookie数据大小超过2048个字节,则旧消息将被丢弃。
  • class storage.fallback.FallbackStorag:此类是django的默认功能引擎,优先使用CookieStorage,然后回退到使用SessionStorage来处理单个cookie中无法容纳的消息。 依赖于django.contrib.sessions。这种根据数据大小选择存储方式的这种行为尽可能避免写session。在一般情况下,它应该能提供最佳性能。如果它不适合您的需要,可以更改:MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
  • class storage.base.BaseStorage:要编写自己的存储类,可以在django. controller .messages.storage.base中子类化BaseStorage类,实现_get_store方法。

2,消息等级Message levels

消息框架基于类似于Python日志记录模块的可配置级别架构。 消息级别允许用户按类型对消息进行分组,以便可以在视图和模板中对消息进行过滤或以不同的方式显示。
可以直接从django.contrib.messages导入的内置级别为:

  • DEBUG:10。将在生产部署中被忽略(或删除)的与开发相关的消息
  • INFO:20。提供给用户的信息
  • SUCCESS:25。动作成功,例如 “您的个人资料已成功更新”
  • WARNING:30。没有发生故障,但是可能即将发生
  • ERROR:40。动作不成功或发生了其他一些失败

3,消息标签Message tags

消息标签是消息级别的字符串表示形式,以及直接在视图中添加的所有其他标签。 标签存储在字符串中,并用空格分隔。 通常,消息标记用作CSS类,以根据消息类型自定义消息样式。 默认情况下,每个级别都有一个标记,所有标签如下:

api.py:
__all__ = (
    'add_message', 'get_messages',
    'get_level', 'set_level',
    'debug', 'info', 'success', 'warning', 'error',
    'MessageFailure',
)

add_message

def add_message(request, level, message, extra_tags='', fail_silently=False):
    """
    Attempt to add a message to the request using the 'messages' app.
    """
    try:
        messages = request._messages
    except AttributeError:
        if not hasattr(request, 'META'):
            raise TypeError(
                "add_message() argument must be an HttpRequest object, not "
                "'%s'." % request.__class__.__name__
            )
        if not fail_silently:
            raise MessageFailure(
                'You cannot add messages without installing '
                'django.contrib.messages.middleware.MessageMiddleware'
            )
    else:
        return messages.add(level, message, extra_tags)
  • 为用户添加消息提示。
  • 参数request表示当前用户的请求对象;参数level表示消息级别;参数message表示消息内容;参数extra_tags自定义消息提示标记,可在模板生成上下文。

get_messages

def get_messages(request):
    """
    Return the message storage on the request if it exists, otherwise return
    an empty list.
    """
    return getattr(request, '_messages', [])
  • 获取用户的消息提示。

get_level

def get_level(request):
    """
    Return the minimum level of messages to be recorded.

    The default level is the ``MESSAGE_LEVEL`` setting. If this is not found,
    use the ``INFO`` level.
    """
    storage = getattr(request, '_messages', default_storage(request))
    return storage.level
  • 获取用户所有消息提示最低级别的值。

set_level

def set_level(request, level):
    """
    Set the minimum level of messages to be recorded, and return ``True`` if
    the level was recorded successfully.

    If set to ``None``, use the default level (see the get_level() function).
    """
    if not hasattr(request, '_messages'):
        return False
    request._messages.level = level
    return True
  • 多不同级别的消息提示进行筛选处理,保留比level高的消息。

debug

def debug(request, message, extra_tags='', fail_silently=False):
    """Add a message with the ``DEBUG`` level."""
    add_message(request, constants.DEBUG, message, extra_tags=extra_tags,
                fail_silently=fail_silently)

info

def info(request, message, extra_tags='', fail_silently=False):
    """Add a message with the ``INFO`` level."""
    add_message(request, constants.INFO, message, extra_tags=extra_tags,
                fail_silently=fail_silently)

success:

def success(request, message, extra_tags='', fail_silently=False):
    """Add a message with the ``SUCCESS`` level."""
    add_message(request, constants.SUCCESS, message, extra_tags=extra_tags,
                fail_silently=fail_silently)

warning:

def warning(request, message, extra_tags='', fail_silently=False):
    """Add a message with the ``WARNING`` level."""
    add_message(request, constants.WARNING, message, extra_tags=extra_tags,
                fail_silently=fail_silently)

error:

def error(request, message, extra_tags='', fail_silently=False):
    """Add a message with the ``ERROR`` level."""
    add_message(request, constants.ERROR, message, extra_tags=extra_tags,
                fail_silently=fail_silently)

更改消息标签(可覆盖):

from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
    
    
    messages.INFO: '',
    50: 'critical',
}

配置消息引擎:

settings.py:
# 设置消息框架的功能引擎
# MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
# MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
# FallbackStorage是默认使用,可以无需设置
MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'

三,在视图和模板中使用消息Using messages in views and templates

在视图中使用消息框架为用户添加消息提示并传递给模板,由模板解析消息上下文并生成HTML。
消息框架在视图函数与视图类中有不同使用方式。

1,在视图函数中使用消息框架

1,Adding a message

views.py:
from django.contrib import messages
messages.add_message(request, messages.INFO, 'Hello world.')

一些快捷示例:

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.')

2,Displaying messages

在模板中:

template:
{
    
    % if messages %}
<ul class="messages">
    {
    
    % for message in messages %}
    <li{
    
    % if message.tags %} class="{
    
    { message.tags }}"{
    
    % endif %}>{
    
    {
    
     message }}</li>
    {
    
    % endfor %}
</ul>
{
    
    % endif %}
  • 如果使用上下文处理器,则应使用RequestContext呈现模板。 否则,请确保消息可用于模板上下文。
  • 即使知道只有一条消息,也应该遍历消息序列,因为否则将不会为下一个请求清除消息存储。
  • 上下文处理器还提供了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 %}

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

  • message::消息的实际文本。
  • level:描述消息类型的整数。
  • tags:一个字符串,它将所有消息的标签(extra_tags和level_tag)组合在一起,并用空格分隔。
  • extra_tags:包含此消息的自定义标签的字符串,以空格分隔。 默认情况下为空。
  • level_tag:级别的字符串表示形式。 默认情况下,它是关联常数名称的小写形式,但是如果需要,可以使用MESSAGE_TAGS设置进行更改。

在视图中:

from django.contrib.messages import get_messages

storage = get_messages(request)
for message in storage:
    do_something_with_the_message(message)
  • get_messages()将返回配置的存储后端的实例。

3,Creating custom message levels

消息级别就是整数,因此可以定义自己的级别常量,并使用它们创建更多定制的用户反馈,例如:
在这里插入图片描述
如果需要在HTML或CSS中标识自定义级别,则需要通过MESSAGE_TAGS提供映射。

4,Changing the minimum recorded level per-request

可以通过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)

5,Adding extra message tags

为了更直接地控制消息标签,可以选择向任何添加方法提供一个包含额外标签的字符串:

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

6,Failing silently when the message framework is disabled

如果您正在编写可重用的应用程序(或其他代码),并希望包含消息传递功能,但又不想要求用户在不希望的情况下启用它,则可以传递额外的关键字参数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)

2,在类视图中使用消息框架

class views.SuccessMessageMixin可以向基于FormView的类添加成功消息属性。
get_success_message(cleaned_data)cleaned_data是来自表单的已清理数据,用于字符串格式化。

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

class AuthorCreate(SuccessMessageMixin, CreateView):
    model = Author
    success_url = '/success/'
    success_message = "%(name)s was created successfully"
  • 可以使用%(field_name)s语法对表单中的已清理数据进行字符串插值。 对于ModelForms,如需要访问保存对象的字段,重写get_success_message()方法:
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import ComplicatedModel

class ComplicatedCreate(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,
        )

3,一个完整示例:

1,配置消息引擎:

settings.py:
# 设置消息框架的功能引擎
# MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
# MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
# FallbackStorage是默认使用,可以无需设置
MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'

2,路由:

MyDjango/urls.py:
from django.urls import path, include

urlpatterns = [
    path('', include(('index.urls', 'index'), namespace='index')),
]

index/urls.py:
from django.urls import path
from .views import *

urlpatterns = [
    path('', index, name='index'),
    path('success', success, name='success'),
    path('iClass', iClass.as_view(), name='iClass'),
]

3,视图功能:

from django.shortcuts import render
from .models import PersonInfo
from django.contrib import messages
from django.template import RequestContext
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView

# 视图函数使用消息框架
def index(request):
    # 消息添加方法一
    messages.debug(request, 'debug类型')
    messages.info(request, 'info类型', 'hello!this is a extra_tag')
    messages.success(request, 'success类型')
    messages.warning(request, 'warning类型')
    messages.error(request, 'error类型')
    # 消息添加方法二
    messages.add_message(request, messages.INFO, 'info类型2')
    # 自定义消息类型
    messages.add_message(request, 66, '自定义类型', 'MyDefine')
    # 获取所以消息提示的最低级别
    current_level = messages.get_level(request)
    print(current_level)
    # 获取当前用户的消息提示对象
    mg = messages.get_messages(request)
    print(mg)
    return render(request, 'index.html', locals(), RequestContext(request))


# 视图类使用消息框架
def success(request):
    return render(request, 'index.html', locals(), RequestContext(request))

class iClass(SuccessMessageMixin, CreateView):
    model = PersonInfo
    fields = ['name', 'age']
    template_name = 'iClass.html'
    success_url = '/success'
    success_message = 'created successfully'

4,模型与模板:

index/models.py:
from django.db import models

class PersonInfo(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    age = models.IntegerField()


templates/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>消息提示</title>
</head>
<body>
    {
    
    % if messages %}
        <ul>
            {
    
    % for m in messages %}
                <li>消息内容:{
    
    {
    
     m.message }}</li>
                <div>消息类型:{
    
    {
    
     m.level }}</div>
                <div>消息级别:{
    
    {
    
     m.level_tag }}</div>
                <div>参数extra_tags的值:{
    
    {
    
     m.extra_tags }}</div>
                <div>extra_tags和level_tag组合值:{
    
    {
    
     m.tags }}</div>
            {
    
    % endfor %}
        </ul>
    {
    
    % else %}
        <script>alert('暂无消息');</script>
    {
    
    % endif %}
</body>
</html>


templates/iClass.html:
<!DOCTYPE html>
<html>
<head>
	<title>数据新增</title>
<body>
	<h3>数据新增</h3>
	<form method="post">
		{
    
    % csrf_token %}
		{
    
    {
    
     form.as_p }}
		<input type="submit" value="确定">
	</form>
</body>
</html>

5,运行:
访问http://127.0.0.1:8000/
在这里插入图片描述
访问http://127.0.0.1:8000/iClass
在这里插入图片描述

四,消息到期Expiration of messages

消息在迭代存储实例时被标记为清除(并在处理响应时实际清除)。为了避免清除消息,可以在迭代后将消息存储设置为False:

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

五,并行请求时的表现Behavior of parallel requests

当涉及到来自同一客户端的多个同时请求时,不能保证将消息传递到创建它们的同一窗口中,也不能保证在某些情况下根本不传递消息。 请注意,这在大多数应用程序中通常不是问题,并且在HTML5中将不再存在,在HTML5中,每个窗口/选项卡将具有自己的浏览上下文。

猜你喜欢

转载自blog.csdn.net/dangfulin/article/details/108651106
今日推荐