如何在模板中将数据从 Flask 传递到 JavaScript?

Posted

技术标签:

【中文标题】如何在模板中将数据从 Flask 传递到 JavaScript?【英文标题】:How can I pass data from Flask to JavaScript in a template? 【发布时间】:2012-06-26 01:33:00 【问题描述】:

我的应用调用返回字典的 API。我想将此字典中的信息传递给视图中的 javascript。具体来说,我在 JS 中使用 Google Maps API,所以我想向它传递一个包含长/纬度信息的元组列表。我知道render_template 会将这些变量传递给视图,以便它们可以在 html 中使用,但是我如何将它们传递给模板中的 JavaScript?

from flask import Flask
from flask import render_template

app = Flask(__name__)

import foo_api

api = foo_api.API('API KEY')

@app.route('/')
def get_data():
    events = api.call(get_event, arg0, arg1)
    geocode = event['latitude'], event['longitude']
    return render_template('get_data.html', geocode=geocode)

【问题讨论】:

【参考方案1】:

您可以在模板中的任何位置使用 variable ,而不仅仅是在 HTML 部分中。所以这应该有效:

<html>
<head>
  <script>
    var someJavaScriptVar = ' geocode[1] ';
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode:  geocode[0]  ' + someJavaScriptVar)" />
</body>
</html>

将其视为一个两阶段的过程:首先,Jinja(Flask 使用的模板引擎)生成您的文本输出。这将发送给执行他看到的 JavaScript 的用户。如果您希望 Flask 变量在 JavaScript 中作为数组可用,则必须在输出中生成数组定义:

<html>
  <head>
    <script>
      var myGeocode = [' geocode[0] ', ' geocode[1] '];
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Jinja 还提供了来自 Python 的更高级的构造,因此您可以将其缩短为:

<html>
<head>
  <script>
    var myGeocode = [ ', '.join(geocode) ];
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
</body>
</html>

您还可以使用for 循环、if 语句等等,请参阅Jinja2 documentation 了解更多信息。

另外,看看 Ford 的回答,他指出 tojson 过滤器是 Jinja2's standard set of filters 的补充。

2018 年 11 月编辑:tojson 现在包含在 Jinja2 的标准过滤器集中。

【讨论】:

非常感谢,mensi!这是我最初的想法,但 Flask 的文档并没有明确说明您也可以在 JS 中使用 var 表单。感谢您清除它。 @mea: 你也可以使用模板引擎生成任意的基于文本的文件,我也用它来动态生成 TeX 文件(-> PDF)和电子邮件,它非常通用;) 快速跟进问题:如果我在 JS 中执行 for 循环,我可以在 python 变量中使用 index 变量吗? geocode[i] ? 有道理,但是您发布的解决方案似乎需要我在 JS 数组的内容中手动编码。我希望我可以更以编程方式完成它,以便我可以传递一个可变长度的 Python 列表,并且一个 JS for 循环可以迭代长度,然后将它们附加到 JS 数组中。对不起,如果我没有让自己足够清楚,但我对 JS 和 web 开发者相当陌生。 @RocketPingu 如果你想在一个单独的文件中传递数据,通常使用jsonmodule 以json对象的形式转储你的数据会更容易【参考方案2】:

将几乎所有 Python 对象转换为 JavaScript 对象的理想方法是使用 JSON。 JSON 是一种很好的系统间传输格式,但有时我们会忘记它代表 JavaScript Object Notation。这意味着将 JSON 注入模板与注入描述对象的 JavaScript 代码相同。

Flask 为此提供了一个 Jinja 过滤器:tojson 将结构转储为 JSON 字符串并将其标记为安全,以便 Jinja 不会自动转义它。

<html>
  <head>
    <script>
      var myGeocode =  geocode|tojson ;
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

这适用于任何可序列化 JSON 的 Python 结构:

python_data = 
    'some_list': [4, 5, 6],
    'nested_dict': 'foo': 7, 'bar': 'a string'

var data =  python_data|tojson ;
alert('Data: ' + data.some_list[1] + ' ' + data.nested_dict.foo + 
      ' ' + data.nested_dict.bar);

【讨论】:

下次在 javascript 控制台中获得 Uncaught SyntaxError: Unexpected token &amp; 时试试这个。 我发现这个答案比公认的答案更可靠、更合乎逻辑。 运行代码时出错:TypeError: Object of type Undefined is not JSON serializable【参考方案3】:

在 HTML 元素上使用 data attribute 可以避免使用内联脚本,这反过来意味着您可以使用 stricter CSP rules 来提高安全性。

像这样指定一个数据属性:

<div id="mydiv" data-geocode=' geocode|tojson '>...</div>

然后在静态 JavaScript 文件中访问它,如下所示:

// Raw JavaScript
var geocode = JSON.parse(document.getElementById("mydiv").dataset.geocode);

// jQuery
var geocode = JSON.parse($("#mydiv").data("geocode"));

【讨论】:

【参考方案4】:

或者,您可以添加一个端点来返回您的变量:

@app.route("/api/geocode")
def geo_code():
    return jsonify(geocode)

然后做一个 XHR 来检索它:

fetch('/api/geocode')
  .then((res)=> console.log(res) )

【讨论】:

是的,您可以,并且在某些架构(尤其是 SPA)中,这是正确的处理方式,但请记住,这样做有几个缺点与在提供数据时将数据烘焙到页面中相比:1. 它速度较慢,2. 即使做草率,它也需要更多代码,3. 它引入了至少两个额外的前端状态,您可能需要干净地处理这些状态在您的 UI 中(即 XHR 请求仍在进行中的状态,以及它完全失败的状态),这需要大量额外的 JavaScript 代码并引入额外的潜在错误来源。【参考方案5】:

已经给出了可行的答案,但我想添加一个检查,以防烧瓶变量不可用。 使用时:

var myVariable =  flaskvar | tojson ;

如果有错误导致变量不存在,由此产生的错误可能会产生意想不到的结果。为避免这种情况:

% if flaskvar is defined and flaskvar %
var myVariable =  flaskvar | tojson ;
% endif %

【讨论】:

【参考方案6】:

对于那些想要将变量传递给使用烧瓶获取的脚本的人来说,这只是另一种替代解决方案,我只能通过在外部定义变量然后按如下方式调用脚本来使其工作:

    <script>
    var myfileuri = "/static/my_csv.csv"
    var mytableid = 'mytable';
    </script>
    <script type="text/javascript" src="/static/test123.js"></script>

如果我在test123.js 中输入 jinja 变量,它不起作用,你会得到一个错误。

【讨论】:

-1;这个答案没有意义。我认为(基于措辞 "a script which is sourced using flask" 以及您明显期望您能够在 /static/test123.js 中使用模板变量)您误解了 @987654324 @s 和 srcs 工作。它们不是 Flask 功能。 浏览器在解析这样的脚本时,会发出一个单独的 HTTP 请求来获取该脚本。脚本内容不会被 Flask 烘焙到模板中;事实上,当浏览器甚至请求脚本时,Flask 可能已经完成了将模板化的 HTML 发送到浏览器。【参考方案7】:
<script>
    const geocodeArr = JSON.parse(' geocode | tojson ');
    console.log(geocodeArr);
</script>

这使用 jinja2 将地理编码元组转换为 json 字符串,然后 javascript JSON.parse 将其转换为 javascript 数组。

【讨论】:

【参考方案8】:

嗯,我有一个棘手的方法来完成这项工作。思路如下-

在HTML正文中制作一些不可见的HTML标签,如&lt;label&gt;, &lt;p&gt;, &lt;input&gt;等,并在标签id中制作模式,例如在标签id中使用列表索引,在标签类名称中使用列表值。

这里我有两个相同长度的列表 maintenance_next[] 和 maintenance_block_time[]。我想使用烧瓶将这两个列表的数据传递给 javascript。所以我取了一些不可见的标签标签并将其标签名称设置为列表索引的模式并将其类名设置为索引处的值。

% for i in range(maintenance_next|length): %
<label id="maintenance_next_i" name="maintenance_next[i]" style="display: none;"></label>
<label id="maintenance_block_time_i" name="maintenance_block_time[i]" style="display: none;"></label>
% endfor%

在此之后,我使用一些简单的 javascript 操作在 javascript 中检索数据。

<script>
var total_len =  total_len ;

for (var i = 0; i < total_len; i++) 
    var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
    var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
    
    //Do what you need to do with tm1 and tm2.
    
    console.log(tm1);
    console.log(tm2);

</script>

【讨论】:

【参考方案9】:

有些js文件来自网络或库,不是自己写的。 他们得到的代码是这样的:

var queryString = document.location.search.substring(1);
var params = PDFViewerApplication.parseQueryString(queryString);
var file = 'file' in params ? params.file : DEFAULT_URL;

This method 使js文件不变(保持独立),并正确传递变量!

【讨论】:

以上是关于如何在模板中将数据从 Flask 传递到 JavaScript?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Twig 中将变量从父模板传递到子模板?

VueJS - 在模板中将属性从一个组件传递到另一个组件

如何在Python框架Flask中将图像文件从表单上传到数据库

在流星中将变量从父模板传递到子模板

如何在 VueJS 中将模板(插槽)从祖父组件传递给孙子?

如何在python flask app中将数据从postgresql渲染到csv?