如何使用Flask实现强制页面刷新的最佳方法?

30

背景
我有大量的字段需要从外部进程实时更新。我想定期更新Flask托管的页面,以显示已连接用户的任何更改。理想情况下,整个页面不会刷新,这是类似系统的投诉,而只是更新页面上的一些字段。

现有方向
我目前的想法是使用JavaScript来处理这个问题,但我不确定在使用Flask时是否可能实现这一点。

是否有使用Flask或第三方模块实现此功能的方法?

附加信息
数据将使用各种套接字和串口进行更新。每个接口将运行在自己的线程中,并更新共享内存。请注意,Flask / Web界面对共享内存具有只读写访问权限,可以由其他线程更新。

总客户池不应超过20人。这是一个测试系统的Web界面,通常每次连接只有1-5个人。


你是如何从这个外部进程获取数据的? - Russell Dias
4个回答

21
为了避免刷新整个页面,你需要使用所谓的AJAX。看起来在Flask中实现这很容易。
由于你希望它定期发生,因此需要从javascript的计时器函数中调用你的AJAX函数。
这意味着你只需将Flask页面中的javascript放入计时器调用中即可。
以下是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

1
这给了我一个错误: feed.js:5 未捕获的引用错误:$SCRIPT_ROOT未定义 - Marshall

5

我认为最简单的方法是使用Javascript,就像您在问题中已经建议的那样。在这种情况下,Flask只需传递包含一些Javascript代码以供浏览器执行的HTML文档,因此我不认为这会对Flask造成任何问题。在这个页面上,我找到了一些使用不同组合的示例,例如使用计时器(似乎是您正在寻找的内容)。


谢谢提供这些示例。如果我被迫刷新整个页面,它们将非常有用。您有没有关于如何刷新页面的一部分的想法? - Adam Lewis
要重新加载页面的一部分,您需要使用ajax查询Web服务器以获取该部分页面的更新版本(您还需要在服务器端实现此操作)。 Flask文档中的[jquery](http://flask.pocoo.org/docs/patterns/jquery/)部分应该对开始处理此操作有用。 - jcollado

4
通过使用flask-socketio,可以在Flask中通过WebSockets实现此目标。在示例中,我使用APScheduler作为后台进程,但任何后台进程都可以。这将每4秒更新网页上的价格。
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')

那么 index.html 模板如下:

<!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>

3

不好意思,Flask中没有任何东西可以使此问题比其他解决方案更容易。SO上有一些关于Python实现comet服务器的不错资料。

正如您所提到的,您可以使用JavaScript轮询服务器以获取新数据。但是,如果您有许多用户,则服务器管理可能会很困难。打开并发TCP连接相当昂贵。这也意味着您的UI可能会稍微有些卡顿,因为事情将每秒左右更新一次,而不是在新数据到达服务器时更新。

考虑到这一点,Flask非常适合作为第二选择,因为它非常容易将响应函数附加到单个URL上。主要要注意的是,您应该使用不会在I/O上严重阻塞的函数。长时间运行的函数将占用整个应用程序。

假设您有两个温度计,并且正在使用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)

在JavaScript文件中,您可以编写一些函数来每分钟更新DOM元素的当前温度。这段代码应该能够给您一些构建实际实现的想法:
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);

感谢您的回复。这正是我所担心的(关于“否”)。请查看我的编辑,以获取有关预期用户数量的详细信息。 - Adam Lewis
它们确实有很大帮助。谢谢你的建议,这似乎是我可以快速测试的东西。顺便说一句,我不喜欢你对标题所做的最后一次编辑。我不知道刷新页面的最佳方法,所以我不会想到搜索AJAX或Comet。我觉得其他处于我的情况的人也不会知道这一点。 - Adam Lewis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接