Django视图内线程及请求调用后messages无法保存问题求助
问题描述
我正在开发一套消息/日志系统,用于让前端获取用户发起的线程中进程的进度状态(包括启动内容、当前进度、完成状态及错误信息)。现有日志机制会将信息保存至.log文件,但为了推送给发起请求的用户,我选用了django.messages组件。
Django配置
INSTALLED_APPS = [ ... 'django.contrib.messages', ... ] MIDDLEWARE = [ ... 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ... ] TEMPLATES = [ { ... 'OPTIONS': { 'context_processors': [ ... 'django.contrib.messages.context_processors.messages', ... ], }, }, ] MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
视图代码
from django.contrib import messages from django.contrib.messages import get_messages from django.shortcuts import render, redirect from django.contrib.auth.models import User from threading import Thread from .logging_view_testing import try_catch import logging def index(request): if(True): thread = Thread(target=function_one, args=(request, )) thread.start() message_list = [] storage = get_messages(request) for message in storage: message_list.append(message) storage.used = False tag_filter = request.GET.get('tag', None) if tag_filter: message_list = [message for message in message_list if tag_filter in message.tags.split()] context = { 'username' : username, 'messages': message_list, } return render(request, 'logging.html', context) def function_one(request): try: try_catch(request) raise Exception("error executing function") except Exception as exc: print("0") messages.error(request, f"ERROR: {str(exc)}") print(f"{str(exc)}") logging.debug(exc)
调用的外部函数
from django.contrib import messages import logging def try_catch(request): try: try: message = "try_catch" messages.info(request, f"INFO: {message}") logging.debug(message) # 此代码行之后消息无法保存 response = requests.get('https://', params={}) messages.info(request, "INFO: got response") raise Exception("error executing function") except Exception as exc: messages.error(request, f"ERROR: {str(exc)}") print(f"{str(exc)}") logging.debug(exc) raise Exception("error executing function") except Exception as exc: messages.error(request, f"ERROR: {str(exc)}") print(f"{str(exc)}") logging.debug(exc)
模板代码
<section> {% if messages %} <table class="messages"> <tbody> {% for message in messages %} <tr{% if message.tags %} class="{{ message.tags }}" {% endif %}> <td>{{ message }}</td> </tr> {% endfor %} </tbody> </table> {% endif %} </section>
遇到的异常问题
- messages.debug类型的消息无法保存,而info/success/error类型消息正常;
- 调用
requests.get()后,即使传入的request对象未改变,消息也无法存入用户session存储。
解决方案
问题1:debug类型消息无法保存
Django的messages组件默认消息级别为messages.INFO,会自动过滤DEBUG级别的消息。只需在settings.py中添加配置,开启DEBUG级别的消息存储:
MESSAGE_LEVEL = messages.DEBUG
问题2:requests.get()后消息无法存入session
核心问题是request对象非线程安全,且子线程操作时机滞后于主线程响应:
- 主线程启动子线程后立即返回响应,此时session已被主线程处理完毕,子线程后续的消息写入无法同步到session;
requests.get()本身不会修改request对象,但子线程操作request的时机错误,加上request对象的线程不安全特性,导致消息丢失。
推荐解决方式:
放弃在子线程中直接操作request和django.messages,改用缓存/消息队列存储进度消息,前端通过轮询或WebSocket获取:
# 子线程中用缓存存储消息 from django.core.cache import cache import uuid import requests def index(request): # 生成唯一任务ID,传给前端用于轮询 task_id = str(uuid.uuid4()) thread = Thread(target=function_one, args=(request.user.id, task_id)) thread.start() context = { 'username': request.user.username, 'task_id': task_id } return render(request, 'logging.html', context) def function_one(user_id, task_id): cache_key = f"task_{task_id}_messages" try: # 存储初始消息 cache.append(cache_key, {"type": "info", "content": "try_catch"}) # 执行请求 response = requests.get('https://example.com', params={}) cache.append(cache_key, {"type": "info", "content": "got response"}) except Exception as exc: cache.append(cache_key, {"type": "error", "content": str(exc)})
然后新增一个接口供前端轮询,从缓存中读取对应task_id的消息并返回。
临时兼容方案(不推荐):
若坚持使用django.messages,需在子线程操作消息后手动保存session,且确保主线程等待子线程完成(但会阻塞请求,失去多线程意义):
def function_one(request): try: try_catch(request) raise Exception("error executing function") except Exception as exc: messages.error(request, f"ERROR: {str(exc)}") # 手动保存session request.session.save()
额外代码修复
- 视图中
username变量未定义,需改为request.user.username; try_catch函数中缺少requests导入,需添加import requests。
内容的提问来源于stack exchange,提问作者Wafiat
相关产品推荐
相关产品推荐

