You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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对象非线程安全,且子线程操作时机滞后于主线程响应:

  1. 主线程启动子线程后立即返回响应,此时session已被主线程处理完毕,子线程后续的消息写入无法同步到session;
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 15:53:14