What is the best way to implement a forced page refresh using Flask?

36,389

Solution 1

To avoid refreshing the entire page you want to use what is called AJAX. It looks like this is easy to implement in flask.

Since you want it to happen periodically you need to call your AJAX functions from a timer function in javascript.

This means you just put the javascript from the flask page inside a timer call.

Here's approximately what the javascript would look like:

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

Solution 2

I think probably the easiest way to do this is using javascript as you already suggest in your question. In this case, Flask would be just delivering an HTML document that contains some javascript code to be executed by the browser, so I don't see why this could cause any problem to Flask. In this page, I've found some examples using different combinations such as using a timer (which seems to be what you're looking for).

Solution 3

No. Well, as least there's nothing in side of Flask that would make this easier than other solutions. SO has some decent material on implementing comet servers in Python.

As you mentioned, there you can use JavaScript to poll the server for new data. This can be difficult for your server to manage though if you have many users. Opening concurrent TCP connections is fairly expensive. It also means that your UI may appear slightly jerky, because things will be updating every second or so, rather than when new data hits the server.

With that in mind, Flask is excellent for this second choice because it is so easy to attach response functions to individual URLs. The main thing to note is you should functions which block heavily on I/O. Long-running functions will seize the whole application.

Let's say you have two temperature gauges & you're using 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)

In a JavaScript file, you could have some function that updates a DOM element every minute with the current temperature. This code should give you some idea of something you can build into an actual implementation:

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);

Solution 4

One way to achieve this is in Flask through WebSockets using flask-socketio. I use APScheduler for the background process in the example, but any background process will do. this updates a price on the webpage every 4 seconds:

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('/')
def index():
    return render_template('index.html')

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

Then the index.html template is:

<!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="//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>
Share:
36,389

Related videos on Youtube

jenny
Author by

jenny

I am an agile software developer who loves to make ideas come to life. My experience ranges from embedded software written in C all the way up to responsive web development. Python, C, JavaScript, and HTML are my most fluent languages that have been honed over 7+ years of professional development. When my top programing languages don't make sense for a given project, I fall back on my experience in C# (.NET), C++ and VB.NET to get the job done. I pride my self in creating clean, efficient, and testable code. I've worked for small companies my entire career and am comfortable working collaboratively or solo to get things done.

Updated on July 18, 2021

Comments

  • jenny
    jenny almost 3 years

    Background
    I have a large number of fields that will be updating real time from an external process. I would like to update the Flask hosted pages periodically to show connected users any changes. Ideally the whole page would not refresh, this was a complaint of a similar system, but rather just update a number of fields on the page.

    Current Direction
    My current thoughts is to use possibly use a JavaScript to handle this, but I'm not sure if that's even possible when using Flask.

    Is there a way with Flask, or 3rd party module, to accomplish this?

    Additional Information
    The data will be updated using various sockets and serial ports. Each interface will be running in its own thread and updating shared memory. Note that the Flask / web interface has read-only writes to shared memory that can be updated by the other threads.

    The total client pool should never exceed 20 people. This is a web interface to a test system and in general will only have 1-5 people connected to it at any given time.

    • Russell Dias
      Russell Dias over 12 years
      How are you getting data from this external process?
  • jenny
    jenny over 12 years
    Thanks for the examples. If I am forced to refresh the entire page, I they will become very useful. Do you have any ideas on how to refresh a portion of the page?
  • jenny
    jenny over 12 years
    Thanks for the input. This is kinda what I was afraid of (regarding the "No"). Check out my edit for details on expected number of users.
  • jcollado
    jcollado over 12 years
    To reload just a portion of a page you'll need to use ajax to query the web server for the updated version of that portion of the page (and you'll need to implement that on the server side as well). The jquery section in the Flask documentation should useful to start with this.
  • jenny
    jenny over 12 years
    They help quite a bit. Thanks for the input, this appears to be something that I can test out quickly. On a side note, I'm not a fan of the last edit you made to the title. I had no idea on the best way to refresh a page, so I would not think to search for AJAX or Comet. I feel that others in my situation would not know this either.
  • Marshall
    Marshall about 8 years
    This gives me the error: feed.js:5 Uncaught ReferenceError: $SCRIPT_ROOT is not defined