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

如何在before_update中实时展示脚本执行进度?

解决before_update钩子中实时展示脚本执行进度的方案

这个问题我之前做项目时也踩过坑——ORM的before_update钩子是包裹在事务上下文里的,所有数据库操作要等整个钩子执行完毕才会提交,所以你中间存到数据库的进度根本查不到。下面几个优雅的解决方案,你可以根据项目情况选:

方案一:脱离当前事务更新进度

既然当前事务要等所有脚本跑完才提交,那我们可以用独立的数据库连接来更新进度,绕过当前的事务上下文。比如直接执行原生SQL,手动提交这部分操作:

import psycopg2  # 以PostgreSQL为例,其他数据库类似

def update_progress(project_id, current_step, total_steps):
    # 创建独立连接,不要用ORM的连接池
    conn = psycopg2.connect("dbname=your_db user=your_user")
    cur = conn.cursor()
    cur.execute(
        "UPDATE project_progress SET current_step = %s, total_steps = %s WHERE project_id = %s",
        (current_step, total_steps, project_id)
    )
    conn.commit()
    cur.close()
    conn.close()

# 在before_update钩子的脚本循环里调用
def before_update(instance, **kwargs):
    scripts = ["script1.sh", "script2.sh", "script3.sh", "script4.sh", "script5.sh"]
    total = len(scripts)
    for idx, script in enumerate(scripts, 1):
        # 执行脚本
        run_script(script)
        # 更新进度,用独立连接
        update_progress(instance.id, idx, total)

这样每一步的进度更新都会立即提交到数据库,前端可以通过轮询或者接口实时获取。

方案二:将耗时脚本移到异步任务队列

这是更推荐的优雅方案——把before_update里的耗时脚本逻辑剥离到异步任务(比如Celery、RQ)中,让请求快速返回,然后通过WebSocket或轮询实时获取任务进度:

  1. 首先,before_update不再直接执行脚本,而是触发一个异步任务:
from celery import shared_task

@shared_task(bind=True)
def run_project_scripts(self, project_id):
    scripts = ["script1.sh", "script2.sh", "script3.sh", "script4.sh", "script5.sh"]
    total = len(scripts)
    for idx, script in enumerate(scripts, 1):
        # 执行脚本
        run_script(script)
        # 更新任务进度(存在Redis或进度表)
        self.update_state(state='PROGRESS', meta={'current': idx, 'total': total})
    return {'status': 'completed', 'current': total, 'total': total}

def before_update(instance, **kwargs):
    # 触发异步任务,立即返回
    run_project_scripts.delay(instance.id)
    # 这里可以先更新项目状态为"处理中",因为事务会提交
    instance.status = "processing"
  1. 前端可以通过WebSocket监听任务进度,或者调用Celery的任务状态接口,实时展示current/total的进度。

这个方案的好处是不会阻塞请求,用户体验更好,而且任务的失败重试、状态管理都更成熟。

方案三:利用数据库的事件通知(特定数据库支持)

如果你的数据库支持事件通知(比如PostgreSQL的LISTEN/NOTIFY),可以在脚本执行的每一步发送进度通知,后端服务监听这些通知后推送给前端:

def before_update(instance, **kwargs):
    scripts = ["script1.sh", "script2.sh", "script3.sh", "script4.sh", "script5.sh"]
    total = len(scripts)
    for idx, script in enumerate(scripts, 1):
        run_script(script)
        # 发送数据库通知
        instance.objects.raw(f"NOTIFY project_progress, '{instance.id}:{idx}:{total}'")

然后后端启动一个监听进程,收到通知后通过WebSocket推送给前端。这个方案不需要额外的存储,但依赖数据库特性,复杂度稍高。


内容的提问来源于stack exchange,提问作者Anna Matveev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:39:22