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

IT技术 javascript python flask jinja2
2021-01-17 03:18:23

我的应用程序调用返回字典的 API。我想将信息从这个 dict 传递给视图中的 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)
6个回答

您可以{{ 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 文档

另外,看看福特的回答,他指出tojson过滤器是Jinja2 标准过滤器集的补充

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

@mea:您还可以使用模板引擎生成任意基于文本的文件,我也用它来动态生成 TeX 文件(-> PDF)和电子邮件,它非常通用;)
2021-03-12 03:18:23
快速跟进问题:如果我在 JS 中执行 for 循环,我可以在 python 变量中使用 index 变量,例如 {{geocode[i]}} 吗?
2021-03-21 03:18:23
@RocketPingu 如果你想在一个单独的文件中传递数据,通常更容易使用jsonmodule以 json 对象的形式转储你的数据
2021-03-29 03:18:23
这是有道理的,但是您发布的解决方案似乎需要我在 JS 数组的内容中手动编码。我希望我可以更编程地做到这一点,这样我就可以传递一个可变长度的 Python 列表,并且一个 JS for 循环可以遍历整个长度,然后将它们附加到 JS 数组中。对不起,如果我没有让自己足够清楚,但我对 JS 和 Web 开发人员相当友好。
2021-04-02 03:18:23
非常感谢,mensi!这是我最初的想法,但 Flask 的文档并没有明确说明您也可以在 JS 中使用 {{var}} 形式。感谢您解决这个问题。
2021-04-08 03:18:23

将几乎任何 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);
运行代码时出现错误: TypeError: Object of type Undefined is not JSON serializable
2021-03-19 03:18:23
下次Uncaught SyntaxError: Unexpected token &进入 javascript 控制台时试试这个
2021-03-31 03:18:23
我发现这个答案比公认的答案更可靠和合乎逻辑。
2021-04-01 03:18:23

在 HTML 元素上使用数据属性可以避免使用内联脚本,这反过来意味着您可以使用更严格的 CSP 规则来提高安全性。

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

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

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

@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 代码并引入额外的潜在错误源。
2021-04-11 03:18:23

已经给出了有效的答案,但我想添加一个检查,在flask变量不可用的情况下作为故障安全。当您使用:

var myVariable = {{ flaskvar | tojson }};

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

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