在执行long函数之前,Flask不是render_template

时间:2017-09-08 05:14:22

标签: python flask

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

@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的概念,请告诉我。 谢谢!

2 个答案:

答案 0 :(得分:2)

这里有两件事你不知道:

  1. render_template实际做什么
  2. HTTP流程如何工作
  3. 对于1,答案很简单。 render_template将获取要呈现的模板的路径以及要使用该模板呈现模板的上下文,并返回一个字符串。所以它不会对请求做任何事情,它只会根据模板和上下文生成一个字符串(在你的代码示例中,它会将这个呈现的字符串存储在data变量中)。

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

    所以你的路线需要决定要回应什么:它会是一个带有一些内容的200 OK(例如某些HTML)吗?还是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. 您需要正确处理错误。在我的示例中,我始终使用done(这是默认状态代码)返回200 OK,但如果处理中出现问题,您将返回something different并可能返回某种错误消息向用户显示。

答案 1 :(得分:0)

不会退回等待data。根据{{​​3}}提到的

,使用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只能用于将数据流式传输到一个页面而不能用于重定向。