¿Cuál es la mejor manera de implementar una actualización de página forzada usando Flask?

5 minutos de lectura

Avatar de usuario de Adam Lewis
adam lewis

Fondo

Tengo una gran cantidad de campos que se actualizarán en tiempo real desde un proceso externo. Me gustaría actualizar las páginas alojadas de Flask periódicamente para mostrar cualquier cambio a los usuarios conectados. Idealmente, toda la página no se actualizaría, esto era una queja de un sistema similar, sino que solo actualizaría una cantidad de campos en la página.

Dirección actual

Mi pensamiento actual es usar posiblemente un JavaScript para manejar esto, pero no estoy seguro de si eso es posible cuando se usa Flask.

¿Hay alguna manera con Flask o un módulo de terceros para lograr esto?

información adicional

Los datos se actualizarán utilizando varios sockets y puertos serie. Cada interfaz se ejecutará en su propio subproceso y actualizará la memoria compartida. Tenga en cuenta que la interfaz Flask/web tiene escrituras de solo lectura en la memoria compartida que los otros subprocesos pueden actualizar.

El grupo total de clientes nunca debe exceder las 20 personas. Esta es una interfaz web para un sistema de prueba y, en general, solo tendrá de 1 a 5 personas conectadas en un momento dado.

  • ¿Cómo obtiene datos de este proceso externo?

    –Russell Dias

    12 de diciembre de 2011 a las 6:01

Para evitar actualizar toda la página, debe usar lo que se llama AJAX. Parece que esto es fácil de implementar en matraz.

Como desea que suceda periódicamente, debe llamar a sus funciones AJAX desde un Temporizador función en javascript.

Esto significa que simplemente coloca el javascript de la página del matraz dentro de una llamada de temporizador.

Así es como se vería aproximadamente el javascript:

setInterval(                               //Periodically 
  function()
  {
     $.getJSON(                            //Get some values from the server
        $SCRIPT_ROOT + '/get_values',      // At this URL
        {},                                // With no extra parameters
        function(data)                     // And when you get a response
        {
          $("#result").text(data.result);  // Write the results into the 
                                           // #result element
        });
  },
  500);                                    // And do it every 500ms

  • Esto me da el error: feed.js: 5 Error de referencia no detectado: $ SCRIPT_ROOT no está definido

    – marshall

    24 de marzo de 2016 a las 14:05

Creo que probablemente la forma más fácil de hacer esto es usar javascript como ya sugieres en tu pregunta. En este caso, Flask solo estaría entregando un documento HTML que contiene algún código javascript para ser ejecutado por el navegador, por lo que no veo por qué esto podría causarle algún problema a Flask. En esta páginaencontré algunos ejemplos que usan diferentes combinaciones, como usar un temporizador (que parece ser lo que está buscando).

  • Gracias por los ejemplos. Si me veo obligado a actualizar toda la página, se volverán muy útiles. ¿Tiene alguna idea sobre cómo actualizar una parte de la página?

    – Adán Lewis

    12 de diciembre de 2011 a las 6:13

  • Para recargar solo una parte de una página, deberá usar ajax para consultar al servidor web la versión actualizada de esa parte de la página (y también deberá implementar eso en el lado del servidor). El jquery La sección en la documentación de Flask debería ser útil para comenzar con esto.

    – jcollado

    12 de diciembre de 2011 a las 6:25

Una forma de lograr esto es en Flask a través de WebSockets usando matraz-socketio. yo suelo Programador de APS para el proceso en segundo plano en el ejemplo, pero cualquier proceso en segundo plano servirá. esto actualiza un precio en la página web cada 4 segundos:

from flask import Flask, render_template
from apscheduler.schedulers.background import BackgroundScheduler
from flask_socketio import SocketIO, emit

app = Flask(__name__)
socketio = SocketIO(app)

#defines the job
def job():
    new_price = get_new_price();
    #job emits on websocket
    socketio.emit('price update',new_price, broadcast=True)

#schedule job
scheduler = BackgroundScheduler()
running_job = scheduler.add_job(job, 'interval', seconds=4, max_instances=1)
scheduler.start()

@app.route("https://stackoverflow.com/")
def index():
    return render_template('index.html')

if __name__ == '__main__':
    socketio.run(app, host="0.0.0.0")

Entonces el índice.html la plantilla es:

<!DOCTYPE HTML>
<html>
<head>
    <title>WebSockets Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
    <script type="text/javascript" charset="utf-8">
       $(document).ready(function(){

           var socket = io.connect('http://' + document.domain + ':' + location.port);

           socket.on('connect', function() {
               socket.emit('am up', {data: 'I\'m connected!'});
           });
           //listens to 'price update' message on socket
           socket.on('price update', function(msg) {
               $('#price_info').text(msg)
           });

       });
   </script>
</head>
<body>
  <div id="price_info"></div>
</body>
</html>

Avatar de usuario de Tim McNamara
Tim McNamara

No. Bueno, al menos no hay nada dentro de Flask que lo haga más fácil que otras soluciones. SO tiene material decente sobre la implementación de servidores Comet en Python.

Como mencionó, allí puede usar JavaScript para sondear el servidor en busca de nuevos datos. Sin embargo, esto puede ser difícil de administrar para su servidor si tiene muchos usuarios. Abrir conexiones TCP concurrentes es bastante costoso. También significa que su interfaz de usuario puede parecer un poco irregular, porque las cosas se actualizarán cada segundo más o menos, en lugar de cuando los nuevos datos lleguen al servidor.

Con eso en mente, Flask es excelente para esta segunda opción porque es muy fácil adjuntar funciones de respuesta a URL individuales. Lo principal a tener en cuenta es que debe tener funciones que bloqueen fuertemente las E/S. Las funciones de ejecución prolongada aprovecharán toda la aplicación.

Digamos que tiene dos indicadores de temperatura y está usando jQuery.

@app.route('/gauge/<int:gauge_id>')
def gauge_temp(gauge_id):
    temp = temp_from_db(gauge_id) #implement this yourself
    return dict(temp=temp, gauge_id=gauge_id)

En un archivo JavaScript, podría tener alguna función que actualice un elemento DOM cada minuto con la temperatura actual. Este código debería darle una idea de algo que puede construir en una implementación real:

function updateTemp(gauge_id, selector) {
  $.getJSON('/gauge/' + gauge_id, function(data){
    $(selector).text = response.temp;
  })
}

setInterval('updateTemp(1, "#gauge-1")', 1000 * 60);
setInterval('updateTemp(2, "#gauge-2")', 1000 * 60);

¿Ha sido útil esta solución?