Python 在执行long函数之前,Flask不在呈现_模板中

Python 在执行long函数之前,Flask不在呈现_模板中,python,flask,Python,Flask,我想创建一个等待页面,在执行一些长函数之前可以呈现该页面。当它完成时,它将重定向到success页面作为回报。这是一个简单的代码示例 @app.route("/process", methods=['GET', 'POST']) def process(): with app.app_context(): data = render_template('waiting.html') ### .. Do something long and have to wai

我想创建一个等待页面,在执行一些长函数之前可以呈现该页面。当它完成时,它将重定向到success页面作为回报。这是一个简单的代码示例

@app.route("/process", methods=['GET', 'POST'])
def process():
    with app.app_context():
        data = render_template('waiting.html')

    ### .. Do something long and have to wait .. ###
    # e.g. sleep a few seconds

    return redirect(url_for('success'))
发生的情况是,
data=render\u template('waiting.html')
行似乎没有执行。我看到的是一段时间的空白屏幕(正如下面的处理功能)。并且,当它完成时,会按预期显示成功页面

这似乎是一项简单的任务,以前这里已经提出了许多问题。但这些解决方案对我不起作用。我的目标只是在执行任务之前显示模板。这样用户就可以看到他们的请求正在进行中

这是我试过的

  • 我在上一个项目中考虑了芹菜。但是我发现它对于这个简单的工作来说太过合适了。所以我怀疑芹菜是一个体面的选择
如果我误解了烧瓶的概念,请告诉我。
谢谢

这里您缺少两件事:

  • “渲染”模板的实际功能是什么
  • HTTP流的工作原理
  • 对于我来说,答案很简单
    render_template
    将获取要呈现的模板的路径和要呈现模板的上下文,并返回字符串。因此,它不会对请求执行任何操作,它只会根据模板和上下文生成一个字符串(在您的代码示例中,它会将这个呈现的字符串存储在
    数据
    变量中)

    对于2,您需要了解的是,当用户向您的
    /process
    路由发出请求时,它将期望得到一个响应,并且只有一个响应

    因此,您的路线需要决定响应什么:它是否是一个
    200OK
    ,包含一些内容(例如一些HTML)?或者一个
    301重定向
    ,它将指示用户的浏览器转到另一个页面?不能同时有两个响应,因为它违反了协议

    为了获得您想要的体验,您必须指示用户的浏览器向您的服务器发出多个请求。有几种策略可以用于此,但最简单的可能是使用Ajax

    这个想法是,您的初始路由将只呈现
    waiting.html
    页面,但会有一些扭曲。在该页面中,您将添加一些JavaScript代码,这些代码向执行实际长时间运行任务的不同路径发出Ajax请求

    现在有两条路线:

    @app.route("/process", methods=['GET', 'POST'])
    def process():
        if request.method == 'GET':
            return render_template('waiting.html')
    
        if request.method == 'POST':
    
            ### .. Do something long and have to wait .. ###
            # e.g. sleep a few seconds
    
            return 'done'
    
    (尽管这只是一种方法,但它们本质上是两条路线:
    GET/process
    POST/process

    页面中的JavaScript将如下所示:

    var request = new XMLHttpRequest();
    request.open('POST', '/process');
    
    request.onload = function() {
      if (request.status === 200 && request.responseText === 'done') {
        // long process finished successfully, redirect user
        window.location = '/success';
      } else {
        // ops, we got an error from the server
        alert('Something went wrong.');
      }
    };
    
    request.onerror = function() {
      // ops, we got an error trying to talk to the server
      alert('Something went wrong.');
    };
    
    request.send();
    
    (或如果您正在使用)

    它将在后台向服务器发起另一个HTTP请求,启动长时间运行的作业,并将收到作为响应的文本
    done
    。发生这种情况时,它会将用户重定向到
    /success
    页面

    现在,重要的是要记住,这是一个非常简单的问题,你需要记住一些事情:

  • 这些Ajax请求中的每一个都会阻止与服务器的连接,如果您使用的是gunicorn之类的东西,那么服务器将是一个完整的工作程序。因此,如果您的处理时间过长,或者如果有很多并发用户访问此路由,那么您的服务器将非常过载

  • 您需要正确处理错误。在我的示例中,我总是返回
    done
    ,并带有
    200ok
    (这是默认状态代码),但如果处理过程中出现问题,您将返回并可能向用户显示某种错误消息


  • 等待
    数据
    不会返回。根据上面提到的方法,使用
    yield
    尝试此解决方案

    from flask import Response, stream_with_context
    
    @app.route("/process", methods=['GET', 'POST'])
    def process():
        def generate_output(): 
            with app.app_context():
                yield render_template('waiting.html')
    
            ### .. Do something long and have to wait .. ###
            # e.g. sleep a few seconds
    
            # Later to find that we Can't yield redirect
            # yield redirect(url_for('success'))
    
        return Response(stream_with_context(generate_output()))
    

    更新:很抱歉发现
    收益
    只能用于将数据流传输到一个页面,而不能用于重定向。

    令人惊讶的是,我终于看到了等待页面。不过,我得到了断言错误:应用程序必须在最后写入字节。我不知道为什么。谢谢你的友好回复。不管怎样,知道这一点很好:)也许它可以与少量javascript配合使用
    yield
    在需要缓冲响应时非常有用。这将不允许您做出两个回复。很抱歉回复太晚,谢谢您的明确解释。我确实误解了render_模板。现在我明白你关于HTTP响应的观点了。事实上,从那天起我就试过你的答案了。但我不得不修改我的代码在其他部分一点,使之工作。我会让你知道结果的!你的代码做了一个诡计!在我修改了请求和javascript之后。它就像一个符咒。我真的很感谢你的解释。再次感谢你!我想问另一个问题。如果我想将进度更新到模板,例如子任务2/5。你能建议我该怎么做吗?这很难在评论中回答,所以我建议你提出一个新问题。不过,你也有一些选择,我建议你在谷歌上搜索一些技术,比如WebSocket(可能有点过头了)、服务器发送事件(SSE)(不流行,所以不太流行)、轮询和长轮询(需要你在服务器中保持状态)。