Django Message 组件使用方法源码分析

[Django Message超全总结教程]

1.使用方法

1.1 基础配置

INSTALLED_APPS = [
    ...
    'django.contrib.messages',
    ...
]
# 在django setting.py 取消注释的message app
MIDDLEWARE = [
    ...
    'django.contrib.messages.middleware.MessageMiddleware',
    ...
]
# 在django setting.py 取消注释的message 的中间件
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ...
                'django.contrib.messages.context_processors.messages',
            ],
        }
    }
]
# 在django setting.py 添加message template设置
MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"
# 在django setting.py 添加 MESSAGE_STORAGE 配置
# 可用配置为 SessionStorage, CookieStorage, FallbackStorage
# SessionStorage: 设置的Message 存储于Session中
# CookieStorage: 设置的Message 存储于Cookie中
# FallbackStorage: 设置的Message 存储于Cookie和Session中

1.2 代码使用

#添加message
from django.contrib import messages
def concel_order(request):
    messages.add_message(request, messages.SUCCESS, "删除成功1")
    messages.add_message(request, messages.SUCCESS, "删除成功2")
    
    return redirect("/order/control/")
# 在视图函数中添加messages模块
# 再通过messages.add_message导入提示信息
# 在视图函数中import get_messages模块获取添加的提示信息
def control_order(request):
    if request.method == "GET":
        from django.contrib.messages.api import get_messages
        m1 = get_messages(request)
        print(m1)
# 在html模板中添加for循环拿到message
<div>
    {% for obj in messages %}
        <ul>{{ obj.message }}</ul>
    {% endfor %}
</div>

2.源码流程

2.1 中间件process_request

# 在html模板中添加for循环拿到message
MIDDLEWARE = [
    ....
    'django.contrib.messages.middleware.MessageMiddleware',
    ....
]
# 在中间件process_request中定义了request._messages = default_storage(request)
# 接着看default_storage是什么
from django.contrib.messages.storage import default_storage
class MessageMiddleware(MiddlewareMixin):
    """
    Middleware that handles temporary messages.
    """

    def process_request(self, request):
        request._messages = default_storage(request)
# default_storage 写入了函数import_string方法 就等于
# setting配置文件中
# MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"
# 等于 SessionStorage(request)
from django.conf import settings
from django.utils.module_loading import import_string


def default_storage(request):
    return import_string(settings.MESSAGE_STORAGE)(request)

现在 request._messages = SessionStorage(request)

2.2 视图函数views.py, 添加message

from django.contrib import messages
def concel_order(request):
    messages.add_message(request, messages.SUCCESS, "删除成功1")
    messages.add_message(request, messages.SUCCESS, "删除成功2")

    return redirect("/order/control/")
def add_message(request, level, message, extra_tags="", fail_silently=False):
    """
    Attempt to add a message to the request using the 'messages' app.
    """
    try:
        # 尝试拿到request._messages
        messages = request._messages
    except AttributeError:
        # 如果失败看request 有没有META参数如果没有说明request对象不是HttpRequest对象报错
        if not hasattr(request, "META"):
            raise TypeError(
                "add_message() argument must be an HttpRequest object, not "
                "'%s'." % request.__class__.__name__
            )
        # 如果 fail_silently = False说明MessageMiddleware中间件未安装,报错
        if not fail_silently:
            raise MessageFailure(
                "You cannot add messages without installing "
                "django.contrib.messages.middleware.MessageMiddleware"
            )
    else:
        # 如果没有报错调用 request._messages.add(level, message, extra_tags)
        # 等于 SessionStorage(request).add(level, message, extra_tags)
        return messages.add(level, message, extra_tags)
# SessionStorage的init方法先调用super().__init__(request, *args, **kwargs) 传入参数 super等于BaseStorage类
# SessionStorage 中没有add方法,所以看父类(BaseStorage)中寻找add方法
class SessionStorage(BaseStorage):
    """
    Store messages in the session (that is, django.contrib.sessions).
    """

    session_key = "_messages"

    def __init__(self, request, *args, **kwargs):
        if not hasattr(request, "session"):
            raise ImproperlyConfigured(
                "The session-based temporary message storage requires session "
                "middleware to be installed, and come before the message "
                "middleware in the MIDDLEWARE list."
            )
        super().__init__(request, *args, **kwargs)
class BaseStorage:
    def __init__(self, request, *args, **kwargs):
        self.request = request
        self._queued_messages = []
        self.used = False
        self.added_new = False
        super().__init__(*args, **kwargs)
    def add(self, level, message, extra_tags=""):
        # messages.add_message(request, messages.SUCCESS, "删除成功1") = return messages.add(level=messages.SUCCESS, message="删除成功1", extra_tags) 
        # 如果这里的 message是空,跳过添加message
        if not message:
            return
        # Check that the message level is not less than the recording level.
        # message.SUCCESS = 25
        level = int(level)
        # self.level 还未定义跳过
        if level < self.level:
            return
        # Add the message.
        # 添加message, 设置 self.added_new = True, 调用Message类传入参数
        self.added_new = True
        # 在下一段代码中为Message源码类 中的__init__方法中添加信息
        message = Message(level, message, extra_tags=extra_tags)
        # 类中append, self._queued_messages = [Message(message.SUCCESS, "删除成功1"), Message(message.SUCCESS, "删除成功2")]
        self._queued_messages.append(message)
class Message:
    """
    Represent an actual message that can be stored in any of the supported
    storage classes (typically session- or cookie-based) and rendered in a view
    or template.
    """

    def __init__(self, level, message, extra_tags=None):
        self.level = int(level)
        self.message = message
        self.extra_tags = extra_tags

2.3 视图函数views.py, 提取message

def control_order(request):
    if request.method == "GET":
        from django.contrib.messages.api import get_messages
        m1 = get_messages(request)
        print(m1)
# get_messages(request) 等于 request._messages 等于 SesssionStorage对象
def get_messages(request):
    """
    Return the message storage on the request if it exists, otherwise return
    an empty list.
    """
    return getattr(request, "_messages", [])
# 在html中for循环, 对象被循环需要执行对象中的iter方法
<div>
    {% for obj in messages %}
        <ul>{{ obj.message }}</ul>
    {% endfor %}
</div>
    # iter魔法方法中, 如果self._queued_messages有值, 
    # self._queued_messages = [Message(message.SUCCESS, "删除成功1"), Message(message.SUCCESS, "删除成功2")]
    # self._loaded_messages.extend 所以 self._loaded_messages = [Message(message.SUCCESS, "删除成功1"), Message(message.SUCCESS, "删除成功2")]
    # return iter(self._loaded_messages) 就等于循环[Message(message.SUCCESS, "删除成功1"), Message(message.SUCCESS, "删除成功2")]列表

    def __iter__(self):
        self.used = True
        if self._queued_messages:
            self._loaded_messages.extend(self._queued_messages)
            self._queued_messages = []
        return iter(self._loaded_messages)
    @property
    def _loaded_messages(self):
        """
        Return a list of loaded messages, retrieving them first if they have
        not been loaded yet.
        """
        # self._loaded_data 为空的 if成立
        if not hasattr(self, "_loaded_data"):
            # messages, all_retrieved = self._get()的返回值需要看一下_get方法
            messages, all_retrieved = self._get()
            self._loaded_data = messages or []
        return self._loaded_data
    def _get(self, *args, **kwargs):
        # self.session_key = "_messages"
        # self.deserialize_messages(self.request.session.get(_messages)),
        # 从session中拿到 key = _messages的value, 再解序列化
        return (
            self.deserialize_messages(self.request.session.get(self.session_key)),
            True,
        )

2.4 中间件process_response

def process_response(self, request, response):
    if hasattr(request, "_messages"):
        # 删除session中存储的message信息
        unstored_messages = request._messages.update(response)
        if unstored_messages and settings.DEBUG:
            raise ValueError("Not all temporary messages could be stored.")
    return response
posted @ 2023-10-20 11:30  khalil12138  阅读(9)  评论(0编辑  收藏  举报