【问题标题】:Flask is not render_template before executing long functionFlask 在执行长函数之前不是 render_template
【发布时间】:2018-02-16 22:38:56
【问题描述】:

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

@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_template('waiting.html') 行似乎没有执行。我看到的是一段时间的空白屏幕(因为它是下面的处理功能)。而且,当它完成时,它会按预期显示成功页面。

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

这是我尝试过的。

如果我误解了 Flask 的概念,请告诉我。 谢谢!

【问题讨论】:

    标签: python flask


    【解决方案1】:

    这里缺少两件事:

    1. render_template 实际做了什么
    2. HTTP 流的工作原理

    对于 1,答案很简单。 render_template 将获取要渲染的模板的路径以及要渲染模板的上下文并返回一个字符串。所以它不会对请求做任何事情,它只会根据模板和上下文生成一个字符串(在您的代码示例中,它会将这个呈现的字符串存储在data 变量中)。

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

    所以你的路由需要决定响应什么:它会是带有一些内容(例如一些 HTML)的200 OK 吗?或301 Redirect 将指示用户的浏览器转到另一个页面?您不能同时拥有这两种响应,因为它违反了协议。

    为了获得您想要的体验,您必须指示用户的浏览器向您的服务器发出多个请求。您可以为此使用一些策略,但最简单的一种可能是使用 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 /processPOST /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();
    

    (或者如果您使用的是jQuery

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

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

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

    2. 您需要正确处理错误。在我的示例中,我总是返回 done200 OK(这是默认状态代码),但如果处理过程中出现问题,您将返回 something different 并且可能会返回某种错误消息以显示给用户。

    【讨论】:

    • 抱歉回复晚了,感谢您的清晰解释。我确实误解了render_template。现在我明白你关于 HTTP 响应的观点了。实际上,从那天起我就尝试过你的答案。但是我不得不在其他部分修改我的代码以使其正常工作。我会让你知道结果!
    • 你的代码可以搞定!在我修改了请求和 javascript 之后。它就像一个魅力。我真的很感谢你的解释。再次感谢您!
    • 我想问另一个问题。如果我想更新模板的进度,例如子任务 2/5。请您建议我该怎么做?
    • 这在评论中很难回答,所以我建议你创建一个新问题。但是你有几个选择,我建议你谷歌一些技术,比如 Websockets(可能有点矫枉过正),服务器发送事件(SSE)(没有流行,所以不是很受欢迎),轮询和长轮询(需要你在您的服务器中保持状态)。
    【解决方案2】:

    等待data 不会返回。根据提到的https://stackoverflow.com/a/31831773/3054766 使用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()))
    

    更新:很遗憾发现yield 只能用于将数据流式传输到一页,而不能用于重定向。

    【讨论】:

    • 太棒了,我终于看到了我的等待页面。不过,最后我得到了AssertionError: applications must write bytes。我不知道为什么。
    • 感谢您的友好回复。很高兴知道这一点:)
    • 也许它可以使用一点 javascript stackoverflow.com/questions/1435015/…
    • yield 在您需要缓冲响应时很有用。它不允许你做出两个回应。
    猜你喜欢
    • 1970-01-01
    • 2013-11-05
    • 2014-06-12
    • 2019-02-22
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多