Redis
 sql >> Base de données >  >> NoSQL >> Redis

Comment renvoyer le flacon render_template une fois le travail d'arrière-plan Redis terminé ?

Une solution basique mais réalisable (essentiel) :

Pour ce faire, vous pouvez simplement rediriger à partir de la route qui met le travail en file d'attente, puis demander à une balise méta d'actualiser cette page périodiquement. Importez d'abord les bibliothèques requises :

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

Configurez les connexions liées à rq et définissez la fonction à exécuter :

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

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

Définissez ensuite un template qui pourra rafraichir la page toutes les 5 secondes :

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

Nous allons également créer une fonction d'assistance pour renvoyer ce modèle avec une variable insérée, en utilisant flask render_template_string . Notez que l'actualisation par défaut est False, si elle n'est pas fournie :

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

Créez maintenant une route qui mettra notre fonction en file d'attente, obtiendra son identifiant de travail rq, puis renverra une redirection vers le result voir avec cet id . Cela prend juste une entrée dans la chaîne d'URL, mais pourrait l'obtenir de n'importe où :

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

Traitons maintenant le résultat réel, à l'aide du rq.Job objet. La logique ici pourrait être modifiée, car cela entraînera une actualisation de la page sur toutes les valeurs sauf "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)

Si le statut est "finished" puis job.result contiendra la valeur de retour de slow_func , nous l'affichons donc sur la page.

Cette méthode a l'inconvénient de provoquer plusieurs requêtes au serveur, en attendant la fin de la tâche. La balise meta refresh peut être un peu non conventionnelle. Si vous envoyez la demande de mise à jour à partir de Javascript, il existe des solutions qui peuvent envoyer la demande AJAX à un intervalle, bien que cela souffre du même problème de demandes multiples.

L'alternative consiste à utiliser des websockets ou SSE pour diffuser le résultat du travail terminé vers le frontend dès qu'il est terminé.

MISE À JOUR :27 février 2021

J'ai décidé d'essayer la méthode SSE de mise à jour de l'interface avec le statut du travail. J'ai appris que rq a un support natif pour mettre à jour un meta attribut dans le travail, en important rq.get_current_job à l'intérieur de la tâche, qui peut ensuite être consultée de l'extérieur après l'actualisation de la tâche.

Voir le code de démonstration pour :

Un exemple de base avec une barre de progression (essentiel) :