How do I return flask render_template after the Redis background job is done?

放肆的年华 提交于 2020-04-11 12:08:28

问题


I have this web app in a flask where I want to execute some ML and AI algorithms after a form is submitted. I am running the ML and AI algorithms in a background job with the help of Redis and rq ( because I have my app hosted by Heroku and they have this timeout thing where you have to return a response within 30 seconds). After the job is done I would like to get the images made by the algorithms ( some graphs ) and output them in a web page, but I have no idea how to render a template in a job function, and importing the app from the flask app to do that doesn't seem to work. Do you have any ideas on how to solve this?

my code fragment from the flask app that enqueues a job:

def upload():
    from mlsalespred import run_model
    file = request.files['file']
    dffile = pd.read_csv(file)
    job = q.enqueue(run_model, dffile)
    return render_template("waiting.html")

my code fragment from the job function:

def run_model(dataFrame):
    - - - - - - - - - - -
    - - some ml stuff - -
    - - - - - - - - - - -
    return render_template("uploaded.html", sales_fig = sales_fig.decode('utf8'), diff_fig = diff_fig.decode('utf8'), pred_fig = pred_fig.decode('utf8') )

Thanks in advance


回答1:


A basic but workable solution (gist):

You could do this by just redirecting from the route which enqueues the job, then have a meta tag refresh that page periodically. First import the required libraries:

from flask import Flask, redirect, url_for, render_template_string
app = Flask(__name__)

from time import sleep

from rq import Queue
from rq.job import Job
from redis import Redis

Set up the rq related connections, and define the function to run:

r = Redis(host='redisserver')
q = Queue(connection=r)

def slow_func(data):
    sleep(5)
    return 'Processed %s' % (data,)

Then define a template which can refresh the page every 5 seconds:

template_str='''<html>
    <head>
      {% if refresh %}
        <meta http-equiv="refresh" content="5">
      {% endif %}
    </head>
    <body>{{result}}</body>
    </html>'''

We'll also make a helper function to return that template with a variable inserted, using flask render_template_string. Notice that refresh defaults to False, if not supplied:

def get_template(data, refresh=False):
    return render_template_string(template_str, result=data, refresh=refresh)

Now make a route which will enqueue our function, get its rq job-id, then return a redirect to the result view with that id. This just takes input in the URL string, but could get that from anywhere:

@app.route('/process/<string:data>')
def process(data):
    job = q.enqueue(slow_func, data)
    return redirect(url_for('result', id=job.id))

Now let's handle the actual result, with the help of the rq.Job object. The logic here could be tweaked, as this will cause a page refresh on all values except "finished":

@app.route('/result/<string:id>')
def result(id):
    job = Job.fetch(id, connection=r)
    status = job.get_status()
    if status in ['queued', 'started', 'deferred', 'failed']:
        return get_template(status, refresh=True)
    elif status == 'finished':
        result = job.result 
        # If this is a string, we can simply return it:
        return get_template(result)

If the status is "finished" then job.result will contain the return value of slow_func, so we render this on the page.

This method has the disadvantage of causing several requests to the server, whilst waiting for job completion. The meta refresh tag may be a bit unconventional. If you're sending the request for an update from Javascript, then there are solutions which can send the AJAX request at an interval, though this suffers from the same multiple request problem.

The alternative is to use websockets, or SSE to stream the result of the completed job to the frontend as soon as it completes.



来源:https://stackoverflow.com/questions/59490025/how-do-i-return-flask-render-template-after-the-redis-background-job-is-done

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