Základní, ale funkční řešení (podstata):
Můžete to udělat pouhým přesměrováním z trasy, která zařazuje úlohu do fronty, a poté nechat metaznačku pravidelně obnovovat tuto stránku. Nejprve importujte požadované knihovny:
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
Nastavte připojení související s rq a definujte funkci, která se má spustit:
r = Redis(host='redisserver')
q = Queue(connection=r)
def slow_func(data):
sleep(5)
return 'Processed %s' % (data,)
Poté definujte šablonu, která může obnovovat stránku každých 5 sekund:
template_str='''<html>
<head>
{% if refresh %}
<meta http-equiv="refresh" content="5">
{% endif %}
</head>
<body>{{result}}</body>
</html>'''
Vytvoříme také pomocnou funkci, která vrátí tuto šablonu s vloženou proměnnou pomocí flask render_template_string
. Všimněte si, že obnovovací výchozí hodnota je False, pokud není zadána:
def get_template(data, refresh=False):
return render_template_string(template_str, result=data, refresh=refresh)
Nyní vytvořte cestu, která zařadí naši funkci do fronty, získá její rq job-id a poté vrátí přesměrování na result
zobrazit s tímto id
. To pouze bere vstup v řetězci adresy URL, ale lze jej získat odkudkoli:
@app.route('/process/<string:data>')
def process(data):
job = q.enqueue(slow_func, data)
return redirect(url_for('result', id=job.id))
Nyní zpracujme skutečný výsledek pomocí rq.Job
objekt. Logiku zde lze vyladit, protože to způsobí obnovení stránky u všech hodnot kromě "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)
Pokud je stav "finished"
pak job.result
bude obsahovat návratovou hodnotu slow_func
, takže to vykreslíme na stránce.
Tato metoda má nevýhodu v tom, že způsobuje několik požadavků na server, zatímco čeká na dokončení úlohy. Meta refresh tag může být trochu netradiční. Pokud posíláte požadavek na aktualizaci z Javascriptu, pak existují řešení, která mohou posílat požadavek AJAX v intervalu, i když to trpí stejným problémem s více požadavky.
Alternativou je použít websockets nebo SSE pro streamování výsledku dokončené úlohy do frontendu, jakmile bude dokončena.
AKTUALIZACE:27. února 2021
Rozhodl jsem se vyzkoušet metodu SSE aktualizace frontendu se stavem úlohy. Naučil jsem se, že rq
má nativní podporu pro aktualizaci meta
atribut v rámci úlohy importem rq.get_current_job
uvnitř úlohy, ke které pak lze přistupovat externě po obnovení úlohy.
Viz ukázkový kód pro:
Základní příklad s ukazatelem průběhu (gist):