显示来自 Flask 视图的更新数据

2024-12-12 08:41:00
admin
原创
143
摘要:问题描述:我有一个视图,可以生成数据并实时传输数据。我不知道如何将这些数据发送到可以在 HTML 模板中使用的变量。我当前的解决方案只是在数据到达时将其输出到空白页,这很有效,但我想将其包含在带有格式的更大页面中。如何在数据流式传输到页面时更新、格式化和显示数据?import flask import tim...

问题描述:

我有一个视图,可以生成数据并实时传输数据。我不知道如何将这些数据发送到可以在 HTML 模板中使用的变量。我当前的解决方案只是在数据到达时将其输出到空白页,这很有效,但我想将其包含在带有格式的更大页面中。如何在数据流式传输到页面时更新、格式化和显示数据?

import flask
import time, math

app = flask.Flask(__name__)

@app.route('/')
def index():
    def inner():
        # simulate a long process to watch
        for i in range(500):
            j = math.sqrt(i)
            time.sleep(1)
            # this value should be inserted into an HTML template
            yield str(i) + '<br/>
'
    return flask.Response(inner(), mimetype='text/html')

app.run(debug=True)

解决方案 1:

您可以在响应中传输数据,但无法按照您描述的方式动态更新模板。模板在服务器端渲染一次,然后发送到客户端。

一种解决方案是使用 JavaScript 读取流式响应并在客户端输出数据。使用 JavaScriptXMLHttpRequest向将流式传输数据的端点发出请求。然后定期从流中读取数据,直到完成。

这带来了复杂性,但允许直接更新页面并完全控制输出内容。以下示例通过显示当前值和所有值的日志来演示这一点。

此示例假设消息格式非常简单:一行数据,后跟换行符。只要有办法识别每条消息,就可以根据需要将其设计得尽可能复杂。例如,每个循环都可以返回客户端解码的 JSON 对象。

from math import sqrt
from time import sleep
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/stream")
def stream():
    def generate():
        for i in range(500):
            yield "{}
".format(sqrt(i))
            sleep(1)

    return app.response_class(generate(), mimetype="text/plain")
<p>This is the latest output: <span id="latest"></span></p>
<p>This is all the output:</p>
<ul id="output"></ul>
<script>
    var latest = document.getElementById('latest');
    var output = document.getElementById('output');

    var xhr = new XMLHttpRequest();
    xhr.open('GET', '{{ url_for('stream') }}');
    xhr.send();
    var position = 0;

    function handleNewData() {
        // the response text include the entire response so far
        // split the messages, then take the messages that haven't been handled yet
        // position tracks how many messages have been handled
        // messages end with a newline, so split will always show one extra empty message at the end
        var messages = xhr.responseText.split('
');
        messages.slice(position, -1).forEach(function(value) {
            latest.textContent = value;  // update the latest value in place
            // build and append a new item to a list to log all output
            var item = document.createElement('li');
            item.textContent = value;
            output.appendChild(item);
        });
        position = messages.length - 1;
    }

    var timer;
    timer = setInterval(function() {
        // check the response for new data
        handleNewData();
        // stop checking once the response has ended
        if (xhr.readyState == XMLHttpRequest.DONE) {
            clearInterval(timer);
            latest.textContent = 'Done';
        }
    }, 1000);
</script>

可以使用An<iframe>来显示流式 HTML 输出,但它有一些缺点。框架是一个单独的文档,这会增加资源使用量。由于它只显示流式数据,因此可能不容易像页面的其余部分那样对其进行样式设置。它只能附加数据,因此长输出将在可见滚动区域下方呈现。它无法响应每个事件来修改页面的其他部分。

index.html使用指向端点的框架呈现页面stream。框架的默认尺寸相当小,因此您可能需要进一步设计它。使用render_template_string知道如何转义变量的 来呈现每个项目的 HTML(或render_template与更复杂的模板文件一起使用)。可以生成初始行以首先在框架中加载 CSS。

from flask import render_template_string, stream_with_context

@app.route("/stream")
def stream():
    @stream_with_context
    def generate():
        yield render_template_string('<link rel=stylesheet href="{{ url_for("static", filename="stream.css") }}">')

        for i in range(500):
            yield render_template_string("<p>{{ i }}: {{ s }}</p>
", i=i, s=sqrt(i))
            sleep(1)

    return app.response_class(generate())
<p>This is all the output:</p>
<iframe src="{{ url_for("stream") }}"></iframe>

解决方案 2:

晚了 5 年,但实际上可以按照您最初尝试的方式完成,javascript 完全没有必要(编辑:在我写完这篇文章后,接受答案的作者添加了 iframe 部分)。您只需将嵌入输出包含为<iframe>

from flask import Flask, render_template, Response
import time, math

app = Flask(__name__)

@app.route('/content')
def content():
    """
    Render the content a url different from index
    """
    def inner():
        # simulate a long process to watch
        for i in range(500):
            j = math.sqrt(i)
            time.sleep(1)
            # this value should be inserted into an HTML template
            yield str(i) + '<br/>
'
    return Response(inner(), mimetype='text/html')


@app.route('/')
def index():
    """
    Render a template at the index. The content will be embedded in this template
    """
    return render_template('index.html.jinja')

app.run(debug=True)

然后 'index.html.jinja' 文件将包含一个<iframe>以内容 url 作为 src 的内容,如下所示:

<!doctype html>
<head>
    <title>Title</title>
</head>
<body>
    <div>
        <iframe frameborder="0" 
                onresize="noresize"
                style='background: transparent; width: 100%; height:100%;' 
                src="{{ url_for('content')}}">
        </iframe>
    </div>
</body>


渲染时应使用用户提供的数据render_template_string()来渲染内容以避免注入攻击。但是,我没有在示例中提及这一点,因为它增加了额外的复杂性,超出了问题的范围,与 OP 无关,因为他没有流式传输用户提供的数据,并且与看到这篇文章的绝大多数人无关,因为流式传输用户提供的数据是一个极端情况,很少有人会这样做。

解决方案 3:

最初我遇到过与此处发布的问题类似的问题,即正在训练模型,更新应保持静止并采用 Html 格式。以下答案供将来参考或供试图解决相同问题并需要灵感的人参考。

实现此目的的一个好方法是使用 Javascript 中的 EventSource,如此处所述。可以使用上下文变量(例如来自表单或其他源)启动此侦听器。通过发送停止命令来停止侦听器。在此示例中,使用 sleep 命令进行可视化而不执行任何实际工作。最后,可以使用 Javascript DOM-Manipulation 实现 Html 格式化。

Flask 应用

import flask
import time

app = flask.Flask(__name__)

@app.route('/learn')
def learn():
    def update():
        yield 'data: Prepare for learning

'
        # Preapre model
        time.sleep(1.0)

        for i in range(1, 101):
            # Perform update
            time.sleep(0.1)
            yield f'data: {i}%

'

        yield 'data: close

'

    return flask.Response(update(), mimetype='text/event-stream')


@app.route('/', methods=['GET', 'POST'])
def index():
    train_model = False

    if flask.request.method == 'POST':
        if 'train_model' in list(flask.request.form):
            train_model = True

    return flask.render_template('index.html', train_model=train_model)

app.run(threaded=True)

HTML 模板

<form action="/" method="post">
    <input name="train_model" type="submit" value="Train Model" />
</form>

<p id="learn_output"></p>

{% if train_model %}
<script>
    var target_output = document.getElementById("learn_output");
    var learn_update = new EventSource("/learn");

    learn_update.onmessage = function (e) {
        if (e.data == "close") {
            learn_update.close();
        } else {
            target_output.innerHTML = "Status: " + e.data;
        }
    };
</script>
{% endif %}
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1590  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1361  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   18  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   18  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   19  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用