How do I change the rendered template in Flask when a thread completes?

↘锁芯ラ 提交于 2021-02-07 08:46:12

问题


I have a function that crawls the web for data and computes a score for the search. However, this can take a while and sometimes the webpage times out before finishing execution.

So I created a separate thread that executes the function and loading.html that tells the client that data is still being collected. Once the function ends in the thread, how do I reload the webpage to display output.html that displays the score.

This is a simpler version of what I have so far:

from flask import Flask
from flask import render_template
from threading import Thread

app = Flask(__name__)

@app.route("/")
def init():
    return render_template('index.html')

@app.route("/", methods=['POST'])
def load():
    th = Thread(target=something, args=())
    th.start()
    return render_template('loading.html')

def something():
    #do some calculation and return the needed value

if __name__ == "__main__":
    app.run()

How do I route my app to render_template('output.html', x=score) once something() inside the thread th finishes?

I am trying to avoid task queues like redis since I want to deploy this app on the web and I don't want to incur charges (this is more of an experiment and hobby).

A detailed answer with code would help a lot since I am new to flask and multithreading


回答1:


An easy way is making cyclic Ajax requests to a thread_status endpoint that gives you information about the currently running task.

import time
from flask import Flask, jsonify
from flask import render_template
from threading import Thread

app = Flask(__name__)
th = Thread()
finished = False


@app.route("/")
def init():
    return render_template('index.html')


@app.route("/", methods=['POST'])
def load():
    global th
    global finished
    finished = False
    th = Thread(target=something, args=())
    th.start()
    return render_template('loading.html')


def something():
    """ The worker function """
    global finished
    time.sleep(5)
    finished = True


@app.route('/result')
def result():
    """ Just give back the result of your heavy work """
    return 'Done'


@app.route('/status')
def thread_status():
    """ Return the status of the worker thread """
    return jsonify(dict(status=('finished' if finished else 'running')))


if __name__ == "__main__":
    app.run(debug=True)

So in your loading.html just insert a cyclic Ajax get() request:

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script>
      $(document).ready(function() {
        var refresh_id = setInterval(function() {
            $.get(
              "{{ url_for('thread_status') }}",
              function(data) {
                console.log(data);
                if (data.status == 'finished') {
                  window.location.replace("{{ url_for('result') }}");
                }
              }
            )}
          , 1000);
      });
    </script>
  </head>
  <body>
    <p>Loading...</p>
  </body>
</html>

You can even append this by a progress counter if you like. But you need to take care that you prevent the thread from being run multiple times.



来源:https://stackoverflow.com/questions/41319199/how-do-i-change-the-rendered-template-in-flask-when-a-thread-completes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!