DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • BBS

  • 订单平台

    • 单点知识
    • 表结构
    • 用户名登陆
    • 短信登陆
    • 菜单和权限
    • 级别管理
    • 客户管理
    • 分页和搜索
    • 价格策略
    • 交易中心
    • message组件
      • 应用场景
      • 快速使用
      • 源码分析
        • 在模版中的配置
        • step1 请求来
        • step2 视图中
        • step3 请求走
        • ★ 总结
      • 撤单
        • 页面提示处理
        • 逻辑处理
    • 我的交易列表
    • worker
    • 部署之代码同步
    • 部署之线上运行
    • 张sir的部署
  • CRM

  • flask+layui

  • django+layui

  • 供应链

  • 实战
  • 订单平台
DC
2023-04-17
目录

message组件

# 应用场景

假设: 你正在做一个订单支付平台, 其中用到了删除/撤销订单问题. 想给予用户一些提示. 可以用到Django的message组件.

该组件通过第一次请求, 写入提示信息并返回重定向, 第二次请求, 呈现提示内容.
建议: 有跳转的时候才使用message, 更多时候通过ajax就可完成!

image-20230413190054105

点击客户列表里某条记录的编辑按钮.
首先要知道 customer/edit/<int:pk>/ 路由地址对应的视图函数customer_edit 里能处理get以及post请求.
1> 先是向这个地址发送get请求 -- http://127.0.0.1:8000/customer/edit/1/ 
2> 在编辑页面点击保存按钮,向该地址发送post请求 -- http://127.0.0.1:8000/customer/edit/1/ 
   因为该地址对应的视图函数customer_edit在处理post请求时,最后返回用到了redirect重定向!!
   So,注意,该post请求的状态是302.
   ★ 将这个地址放到响应头的Location字段中,让浏览器再向这个重定向的地址发送请求!!!
     <并不是>Django直接拿这个地址的内容给浏览器.
3> 浏览器再向该地址发送get请求 http://127.0.0.1:8000/customer/list/

http协议是无状态的短链接. 一次请求和一次响应后,断开连接.(一问一答)
当步骤2中出错了,是没办法在步骤3这个新请求里展示错误信息的!! "或者说, 删除/撤销 订单成功,在重定向的页面上展示,是没有办法的."
因为步骤2的请求和步骤3的请求是两个不同的请求!两者是没有关系的.
1
2
3
4
5
6
7
8
9
10
11
12
13

image-20230413175847031


# 快速使用

message组件在Django中就是一个App

配置: 需要在settings文件中配置4个地方!!

# -- 第1个位置! app
INSTALLED_APPS = [
    ...  ...
    'django.contrib.messages',
]


# -- 第2个位置! 中间件
MIDDLEWARE = [
    ...  ...
    'django.contrib.messages.middleware.MessageMiddleware',
]


# -- 第3个位置! 让我们在html模版里操作比较方便.
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # context_processors选项是一个可调用的列表,里面的项称为上下文处理器
            # 它将一个请求对象作为参数,并返回一个要合并到上下文中的项目字典.
            'context_processors': [
                 ...  ... 
                'django.contrib.messages.context_processors.messages',  # !
            ],
        },
    },
]


# -- 第4个位置 写在settings配置文件的任意地方都可.
#    该配置表明把数据存储在哪里? cookie/session/
#    FallbackStorage --> cookie和session中都保存.(默认) 在Django的原本的setting配置中写的.
# MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'  
# MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

设置值: 在某个视图函数里设置值!!

def my_order_cancel(request, pk):
    """客户撤单"""
    from django.contrib import messages
    # add_message的参数: 当前请求对象、消息的级别、具体的消息
    messages.add_message(request, messages.SUCCESS, "撤单成功1!")
    messages.add_message(request, messages.SUCCESS, "撤单成功2!")
    return redirect(reverse("home"))  # -- 重定向到home页面
1
2
3
4
5
6
7

读取值: 有两个地方可以读取!!
任何视图都可以往里放数据, 任何请求都可以取, 只要该请求没结束, 就可以无限次取.. 该次请求结束, 其他请求想取,没有了..

可以在redirect重定向的路由对应的视图函数中读取.

def home(request):
    # 读取之前页面设置的message
    from django.contrib.messages.api import get_messages
    messages = get_messages(request)
    for msg in messages:
        print(msg)
    return render(request, "home.html")
1
2
3
4
5
6
7

也可以在重定向的路由对应的模版中读取.

def home(request):
    return render(request, "home.html")


{% extends 'layout.html' %}
{% block content %}
    <h3>欢迎登陆!</h3>
    {% for message in messages %}
        <li>{{ message.tags }} {{ message }}</li>
    {% endfor %}
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11

# 源码分析

message组件的源码是很简单的源码啦, 这都看不懂的话, 那就洗洗睡吧..
假比, 现在进行的是一个删除功能!! 用户点击删除按钮, 向 /user/delete/1/ 进行了url 跳转/发起请求..

# 在模版中的配置

是做了怎样的配置, 使得在模版中可以直接使用 message变量 ?? 至于为什么,此处不探究.

"""
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                 ...  ... 
                'django.contrib.messages.context_processors.messages',  # !
            ],
        },
    },
]
"""

1. context_processors选项是一个可调用的列表,里面的项称为上下文处理器
   它将一个请求对象作为参数,并返回一个要合并到上下文中的项目字典.  
   为啥这样设置就可在模版中调用,具体的底层原理此处不探究!简单看了下Django文档.涉及到RequestContext对象.
2. command+点击 messages, 查看该上下文处理器的源码.
源码如下:
from django.contrib.messages.api import get_messages
from django.contrib.messages.constants import DEFAULT_LEVELS


def messages(request):
    # -- 此处return的字典中的"键"可以在任一模版中的作为一个变量直接使用.
    return {
        'messages': get_messages(request),  # 熟悉吗?这个get_messages函数就是我们读取值时调用的函数!!
        'DEFAULT_MESSAGE_LEVELS': DEFAULT_LEVELS,  # 表示级别的常量
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# step1 请求来

执行 message组件的中间件的process_request 方法, 得到一个 SessionStorage类的实例化对象!!
注意哦!! 不一定就是 SessionStorage类的实例化对象, 因为这里MESSAGE_STORAGE使用的是session!!!

简单来说, 请求一来
1> 执行中间件里的process_request方法
2> import_string
3> 实则就是调用SessionStorage类的父类BaseStorage的__init__方法 进行了类的实例化, 封装了一些成员进去.
     该实例化对象赋值给了 request.__message
一开始, request.__message 里有 request=request; _queued_messages = []; used = False; added_new = False

"""
中间件源码
"""
from django.conf import settings
from django.contrib.messages.storage import default_storage
from django.utils.deprecation import MiddlewareMixin

class MessageMiddleware(MiddlewareMixin):
    def process_request(self, request):
        request._messages = default_storage(request)  # !看default_storage方法.

    def process_response(self, request, response):
        if hasattr(request, '_messages'):
            unstored_messages = request._messages.update(response)
            if unstored_messages and settings.DEBUG:
                raise ValueError('Not all temporary messages could be stored.')
        return response
      
      
"""
default_storage方法
"""
from django.conf import settings
from django.utils.module_loading import import_string


def default_storage(request):
    # import_string本质就是 根据字符串的形式找到类
    # from django.contrib.messages.storage.session import SessionStorage
    # SessionStorage(request) 类的实例化,内部封装了几个值.
    return import_string(settings.MESSAGE_STORAGE)(request)  # -- 可扩展性的体现.
  
  
"""
SessionStorage(request) 类的实例化 执行初始化方法__init__
"""
class SessionStorage(BaseStorage):
    session_key = '_messages'

    def __init__(self, request, *args, **kwargs):
        # 断言, assert expression [, arguments]
        # 意味着, hasattr(request, 'session') 为False,就抛出异常信息, “The session-based...”
        # hasattr(request, 'session') 为True, 才会执行 super().__init__(request, *args, **kwargs)
        # 为何这样判断,因为在session中间件的源码是这样定义的. 没有执行session中间件,request对象里肯定没有session这个成员.
        """
        def process_request(self, request):
            session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
            request.session = self.SessionStore(session_key)
        """
        assert hasattr(request, 'session'), "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)
        
        
"""
SessionStorage类的父类BaseStorage类的初始化方法
"""
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)  # BaseStorage的父类就是object类啦!
        

"""
在seetings配置文件中的配置
"""
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

# step2 视图中

开始执行路由对应的视图函数

简单来说, 视图函数里每执行一句 messages.add_message() , 就会创建一个 Message类的实例化对象.
并将这些消息的实例化对象 放到了SessionStorage类的实例化对象的成员 _queued_messages 这个列表里!!
Message类的实例化对象有成员: level = int(level) ; message = message ; extra_tags = extra_tags

"""
视图函数
"""
# path('user/delete/<int:pk>/', user.user_delete, name='user_delete'),
def user_delete(request, pk):
    from django.contrib import messages
    # add_message的参数: 当前请求对象、消息的级别、具体的消息
    messages.add_message(request, messages.SUCCESS, "删除成功1!")  # 查看add_message方法
    messages.add_message(request, messages.SUCCESS, "删除成功2!")
    return redirect(reverse('user_list'))
  
  
"""
add_message方法
"""
Q: command+B跳转过去,该方法的源码在 django.contrib.messages.api 这个py文件里.
   那为啥 用messages.add_message()就可以调用呢?
A: 因为在 django.contrib.messages.__init__ 这个py文件里写了这两行代码.
   from django.contrib.messages.api import *  # NOQA
   from django.contrib.messages.constants import *  # NOQA
  

def add_message(request, level, message, extra_tags='', fail_silently=False):
    """参数:当前请求对象、消息的级别、具体的消息"""
    try:
        # 在中间件的process_request方法里就往当前请求对象request里放入了名为_messages的成员
        # 而且通过分析得知,该成员是一个 SessionStorage(request) 类的实例化对象!!
        messages = request._messages
    # 这里抛错,只有可能是基于 request._messages 这行代码出发.
    # 1> add_message传入的request不是当前请求对象
    # 2> 没有执行message组件的中间件(中间件未注册)导致request对象里没有_messages这个成员.
    #    若调用add_message方法时传入fail_silently=True,那么就不会报这个错啦..静默处理.
    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:
        # messages = request._messages 这行代码未抛错,执行下面这行代码!
        # 前面说了 request._messages是SessionStorage类的实例化对象, 赋值给了 messages变量.
        # So,这里我们去 SessionStorage类 里找add方法, 没找到 就去SessionStorage类的父类BaseStorage中找..
        return messages.add(level, message, extra_tags)
      

"""
add方法
"""
# 视图函数里调用时,是这样传参的 messages.add_message(request, messages.SUCCESS, "删除成功1!")
class BaseStorage:
    def add(self, level, message, extra_tags=''):
        if not message:
            return
        level = int(level)
        # -- 这个无伤大雅,就不细说啦,消息级别是debug级别是不会写入到session中的..
        #    看源码的话,也很容易理解. 注意两个点: 
        #    1> self.level 是用property将方法包装成的数据属性
        #    2> messages.SUCCESS 可以直接用是因为 messages这个包里 有个__init__.py 文件,导入了一些成员.
        if level < self.level:
            return
        self.added_new = True  # 设计一个标记,表示有新增的啦!
        # -- 将要添加的信息打包成一个Message对象!  
        message = Message(level, message, extra_tags=extra_tags)
        # ★ 信息封装成了对象,并保存到了内存中的一个列表里!!!
        self._queued_messages.append(message)
        

"""
Message类的初始化
"""
class Message:
    def __init__(self, level, message, extra_tags=None):
        self.level = int(level)
        self.message = message
        self.extra_tags = extra_tags
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

# step3 请求走

路由对应的视图函数最后执行 return redirect(reverse('user_list')) 返回, 又开始走中间件!

"""
中间件源码 process_response
"""
from django.conf import settings
from django.contrib.messages.storage import default_storage
from django.utils.deprecation import MiddlewareMixin


class MessageMiddleware(MiddlewareMixin):
    def process_request(self, request):
        request._messages = default_storage(request)

    def process_response(self, request, response):
        # 先反射判断request对象里有没有_messages. 都走到这里了,肯定是有的.
        if hasattr(request, '_messages'):
            # 看update方法. 经分析,用的是BaseStorage类中的update方法.
            unstored_messages = request._messages.update(response)  
            if unstored_messages and settings.DEBUG:
                raise ValueError('Not all temporary messages could be stored.')
        return response
      
      
"""
update方法
"""
class BaseStorage:
    def update(self, response):
        self._prepare_messages(self._queued_messages)  # 没啥意义,暂且不看.
        if self.used:
            return self._store(self._queued_messages, response)
        elif self.added_new:
            # -- 把之前的和新增的拿过来进行拼接
            messages = self._loaded_messages + self._queued_messages
            # 调用的是SessionStorage类里的_store方法.
            return self._store(messages, response)


"""
_loaded_messages方法
"""
@property
def _loaded_messages(self):
    # 这里写了个hasattr反射,是为了 视图里多次取时, 不用反复去session中取.
    if not hasattr(self, '_loaded_data'):
        messages, all_retrieved = self._get()
        self._loaded_data = messages or []
    return self._loaded_data

      
"""
_get方法
"""
def _get(self, *args, **kwargs):
    # 从session中取
    return self.deserialize_messages(self.request.session.get(self.session_key)), True

      
"""
_store方法
"""
class SessionStorage(BaseStorage):
    def _store(self, messages, response, *args, **kwargs):
        if messages:
            # -- 将信息序列化后存储到session中!!
            self.request.session[self.session_key] = self.serialize_messages(messages)
        else:
            self.request.session.pop(self.session_key, None)
        return []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

# ★ 总结

2023-03-07-1753

Ps: 图中是message组件关键源码..

看源码心得: 类实例化对象调用某个方法, 先在自己类里找, 没有就去父类里找, 调用父类里的某个self.方法() 时, 也是这个逻辑, 先自己类里找, 没有就父类里找.. 很简单的一个知识点, 但看了源码, 才有深刻的体验..

"""
就只有新增
"""
[step1]
request._messages = SessionStorage(request)
   -- request._messages 成员 --
   request=request
   _queued_messages = []
   used = False
   added_new = False
[step2]
messages = request._messages
messages.add(level, message, extra_tags) 
   -- messages.add(level, message, extra_tags)  即-- 
   message = Message(level, message, extra_tags=extra_tags)
       -- 成员 -- 
       level = int(level)
       message = message 
       extra_tags = extra_tags
messages.added_new = True
messages._queued_messages.append(message)
[step3]
loaded_data = 反序列化(messages.request.session.get("__messages")) or []
messages.request.session["__messages"] = 序列化(loaded_data + messages._queued_messages)



"""
怎么取的,上面没有单独讲解..很简单,懒得补充了.详细阐述的只是新增过程涉及到的源码.
★ 这里阐述下,整体的一个逻辑!!
"""
★★★★★★ *任何视图都可以往里放数据,ABCD任何请求都可以取.
 假设A取,只要A请求没结束,就可以无限次取..在视图中取模版中取! 当A请求结束了,BCD其他请求想取,里面没有东西了..*
▲ 视图:
  增 --> 往_queued_messages列表里添加message对象     add_new = True
  取 --> seesion里 + 新增的 一起迭代出来,<★并>将_queued_messages置为空列表   used = True
▲ 请求走了:
  不管是连续多增、连续多取、先(多)增后(多)取再(多)增
  if used:
      只将最后新增的放到session中,session中"__messages"这个key中只有新增的
  elif add_new:
      session中原来的取出来 + 新增的 重新组合下, 再放到session中.
      
很巧妙的运用 add_new、used 这两个标志!! 
不管是取的时候将 _queued_messages置为空列表, 还是在请求走时, 那个if..elif判断, 都是我应该学习的地方!!!
★ 而且,源码里updata的方法里的英文注释翻译过来:
  可以很简单明了的表述上面的逻辑,只不过第一次看到时,不能很快的转过弯来罢了.
  - ★★★★★★ 储存所有未读消息. 如果后端尚未迭代, 则再次存储以前存储的消息. 否则,只存储在最后一次迭代之后添加的消息.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 撤单

思考:
Q: 在前面新建订单时, 最后一步是 将下单的任务加到redis队列中的.. 那么此处撤单成功后, 需要将此订单从队列中移除吗?
A: No, 不用. 若要进行移除的话, 需要循环队列..较繁琐, 因为后续的worker从订单中拿到一个订单ID后, 会根据订单ID拿到订单的其他详细信息, 拿到后可以做个判断, 订单状态为待执行便执行; 订单状态为已撤单便不管啦!!

# 页面提示处理

1. 在撤单对应的视图函数里得判断该订单是否存在.
   筛选的条件有四个: id=pk, active=1, status=1, customer=request.nb_user.id
2. 在撤单对应的视图函数里,例如message组件写入信息. 并在订单列表页面展示 写入的信息.. 比如:订单不存在、撤单成功.
   注意,在订单页面展示信息时,不同类型的信息提示框的背景色应该是不一样的.
   查询bootstrap官方文档,警告框支持的样式 包括 成功、消息、警告或危险 - success、info、warning、danger
   在message组件的constans.py中 支持的包括 DEBUG、INFO、SUCCESS、WARNING、ERROR 是没有danger的!!
   我们可以扩写:(在页面上是这样的)
   {% for obj in messages %}
        <div class="alert alert-{{ obj.level_tag }}">
            {{ obj.message }}
        </div>
   {% endfor %}
   主要是 obj.level_tag !! 看源码一探究竟,无外乎看 Message类的源码.
   from django.conf import settings
   from django.contrib.messages import constants
   def get_level_tags():
       # 这个有点东西,将多个字典组合成一个字典!!学费了.
       # 本质上最后这个字典 {10:"debug",20:"info"}
       return {
           **constants.DEFAULT_TAGS,
           # 意味着,我们可以在配置文件里扩展!!
           # MESSAGE_DANDER_TAG = 50  MESSAGE_TAGS = {MESSAGE_DANDER_TAG: "danger"}
           # 视图函数里 这样使用: messages.add_message(request, settings.MESSAGE_DANDER_TAG, "订单不存在")
           **getattr(settings, 'MESSAGE_TAGS', {}),
       }
   LEVEL_TAGS = utils.get_level_tags()
   @property
   def level_tag(self):
       # 注意: 在视图函数中 messages.add_message(request, messages.ERROR, "订单不存在")
       # messages.ERROR 这玩意儿,到了Message类里就是self.level 本质就是一个数字罢了.
       return LEVEL_TAGS.get(self.level, '')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 逻辑处理

同样的, 会基于事务基于锁

def my_order_cancel(request, pk):
    """客户撤单"""
    # 0.撤销的订单是否存在
    order_object = models.Order.objects.filter(id=pk, active=1, status=1, customer=request.nb_user.id).first()
    if not order_object:
        messages.add_message(request, settings.MESSAGE_DANDER_TAG, "订单不存在.")
        return redirect(reverse("my_order_list"))

    try:
        with transaction.atomic():
            models.Customer.objects.filter(id=request.nb_user.id).select_for_update().first()  # 单纯的就是想加锁
            # 1.订单状态变化为 (5, "已撤单"),
            # 方式1
            # order_object.status = 5
            # order_object.save()
            # 方式2
            models.Order.objects.filter(id=pk, active=1, status=1, customer=request.nb_user.id).update(status=5)
            # 2.归还扣款到余额中
            models.Customer.objects.filter(id=request.nb_user.id).update(
                balance=F("balance") + order_object.real_price
            )
            # 3.添加一个撤单的交易记录
            models.TransactionRecord.objects.create(
                charge_type=5,
                customer_id=request.nb_user.id,
                amount=order_object.real_price,
                order_oid=order_object.oid
            )
    except Exception as e:
        messages.add_message(request, settings.MESSAGE_DANDER_TAG, "撤单失败,{}".format(str(e)))
        return redirect(reverse("my_order_list"))
    # 撤单成功了
    messages.add_message(request, messages.SUCCESS, "撤单成功")
    return redirect(reverse("my_order_list"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

再次强调:
页面跳转页面会刷新,无法展示错误信息,才用的message组件;
若使用ajax, 一点有错误了, 直接在页面展示即可.. 不存在页面跳转刷新问题.. So, 用ajax就不用message组件.


交易中心
我的交易列表

← 交易中心 我的交易列表→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式