Uma solução básica, mas viável (essência):
Você pode fazer isso apenas redirecionando da rota que enfileira o trabalho e, em seguida, faça uma metatag atualizar essa página periodicamente. Primeiro importe as bibliotecas necessárias:
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
Configure as conexões relacionadas ao rq e defina a função a ser executada:
r = Redis(host='redisserver')
q = Queue(connection=r)
def slow_func(data):
sleep(5)
return 'Processed %s' % (data,)
Em seguida, defina um modelo que possa atualizar a página a cada 5 segundos:
template_str='''<html>
<head>
{% if refresh %}
<meta http-equiv="refresh" content="5">
{% endif %}
</head>
<body>{{result}}</body>
</html>'''
Também faremos uma função auxiliar para retornar esse template com uma variável inserida, usando flask
render_template_string
. Observe que o padrão de atualização é False, se não for fornecido:def get_template(data, refresh=False):
return render_template_string(template_str, result=data, refresh=refresh)
Agora faça uma rota que enfileirará nossa função, obtenha seu rq job-id e retorne um redirecionamento para o
result
view com esse id
. Isso apenas leva a entrada na string de URL, mas pode obtê-la de qualquer lugar:@app.route('/process/<string:data>')
def process(data):
job = q.enqueue(slow_func, data)
return redirect(url_for('result', id=job.id))
Agora vamos lidar com o resultado real, com a ajuda do
rq.Job
objeto. A lógica aqui pode ser ajustada, pois isso fará com que a página seja atualizada em todos os valores, exceto "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)
Se o status for
"finished"
então job.result
conterá o valor de retorno de slow_func
, então renderizamos isso na página. Este método tem a desvantagem de causar vários pedidos ao servidor, enquanto aguarda a conclusão do trabalho. A metatag de atualização pode ser um pouco não convencional. Se você estiver enviando a solicitação de atualização do Javascript, existem soluções que podem enviar a solicitação AJAX em um intervalo, embora isso sofra do mesmo problema de várias solicitações.
A alternativa é usar websockets ou SSE para transmitir o resultado do trabalho concluído para o frontend assim que ele for concluído.
ATUALIZAÇÃO:27 de fevereiro de 2021
Decidi experimentar o método SSE de atualizar o frontend com o status do trabalho. Aprendi que
rq
tem suporte nativo para atualizar um meta
atributo dentro do trabalho, importando rq.get_current_job
dentro do trabalho, que pode ser acessado externamente após a atualização do trabalho. Veja o código de demonstração para: