如何通过 Jinja2 从 Python 传递列表到 JavaScript

IT技术 javascript python variables jinja2
2021-03-09 22:57:42

假设我有一个 Python 变量:

list_of_items = ['1','2','3','4','5']

我通过渲染 HTML 将它传递给 Jinja,我在 JavaScript 中还有一个名为somefunction(variable). 我正在尝试通过list_of_items. 我试过这样的事情:

{% for item in list_of_items %}
<span onclick="somefunction({{item}})">{{item}}</span><br>
{% endfor %}

是否可以将列表从 Python 传递到 JavaScript,还是应该循环传递列表中的每个项目?我怎样才能做到这一点?

6个回答

要将一些上下文数据传递给 javascript 代码,您必须以一种 javascript(即 JSON)可以“理解”的方式对其进行序列化。您还需要使用safeJinja 过滤器将其标记为安全,以防止您的数据被 html 转义。

您可以通过执行以下操作来实现此目的:

风景

import json

@app.route('/')
def my_view():
    data = [1, 'foo']
    return render_template('index.html', data=json.dumps(data))

模板

<script type="text/javascript">
    function test_func(data) {
        console.log(data);
    }
    test_func({{ data|safe }})
</script>

编辑 - 准确答案

因此,要准确实现您想要的(循环遍历项目列表,并将它们传递给 javascript 函数),您需要分别序列化列表中的每个项目。您的代码将如下所示:

风景

import json

@app.route('/')
def my_view():
    data = [1, "foo"]
    return render_template('index.html', data=map(json.dumps, data))

模板

{% for item in data %}
    <span onclick=someFunction({{ item|safe }});>{{ item }}</span>
{% endfor %}

编辑 2

在我的示例中,我使用Flask,我不知道您使用的是什么框架,但是您明白了,您只需要使其适合您使用的框架即可。

编辑 3(安全警告)

切勿使用用户提供的数据执行此操作,仅使用受信任的数据执行此操作!

否则,您的应用程序就会暴露于 XSS 漏洞!

-1; 在一般情况下,这是不安全的,并且只有少数情况下可以使用它。如果任何项目包含空格,输出将被破坏,如果其中之一是," ><script>alert('pwned');</script> "那么你将被 XSS。是的,您有一个安全警告,但即便如此,这也是一个糟糕的答案;有这样说的清洁方法破(如使用瓶的tojson过滤器),这些应改为使用。
2021-04-25 22:57:42
太棒了,它需要使用“{{ data | safe}}”从 jinja 变量加载数据。
2021-05-01 22:57:42
{{ 数据 | safe }} 在列表中为我工作,无需先转换为 json。
2021-05-05 22:57:42
可能更好的是,不是作为 JSON 字符串传递{{item|tojson|safe}}而是执行
2021-05-15 22:57:42

我在使用 Flask 时遇到了类似的问题,但我不必求助于 JSON。我只是通过一个列表letters = ['a','b','c']render_template('show_entries.html', letters=letters)和集

var letters = {{ letters|safe }}

在我的 javascript 代码中。Jinja2 替换{{ letters }}['a','b','c'],javascript 将其解释为字符串数组。

-1; 这不适用于所有值,并且如果任何内容是用户提供的,则很危险。例如,如果您的列表中的字符串之一是,</script><script>alert('pwned')</script>那么它会破坏您的页面。
2021-05-11 22:57:42

你可以用 Jinja 的tojson过滤器来做到这一点,它

将结构转储到 JSON,以便<script>在 HTML 中的任何位置使用标签 [和]是安全的,但双引号属性除外。

例如,在您的 Python 中,编写:

some_template.render(list_of_items=list_of_items)

... 或者,在 Flask 端点的上下文中:

return render_template('your_template.html', list_of_items=list_of_items)

然后在你的模板中,写下这个:

{% for item in list_of_items %}
<span onclick='somefunction({{item | tojson}})'>{{item}}</span><br>
{% endfor %}

(请注意,该onclick属性是引号。这是必要的,因为在其输出中|tojson转义'字符而不是"字符,这意味着它可以安全地用于单引号 HTML 属性中,但不能用于双引号属性。)

或者,要list_of_items在内联脚本中使用而不是 HTML 属性,请这样写:

<script>
const jsArrayOfItems = {{list_of_items | tojson}};
// ... do something with jsArrayOfItems in JavaScript ...
</script>

不要json.dumps在 Python 代码中使用JSON 编码变量并将生成的 JSON 文本传递给模板。这将为某些字符串值产生不正确的输出,并且如果您尝试对用户提供的值进行编码,则会将您暴露给 XSS。这是因为 Python 的内置json.dumps不会转义像<>这样的字符(需要转义以将值安全地模板化为 inline <script>s,如https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for 所述-contents-of-script-elements)或单引号(需要转义以安全地将值模板化为单引号 HTML 属性)。

如果您使用 Flask,请注意 Flask 注入自定义tojson过滤器而不是使用 Jinja 的版本。但是,上面写的所有内容仍然适用。这两个版本的行为几乎相同;Flask 只允许一些Jinja 版本中不可用的特定于应用程序的配置

这个答案似乎比选择的答案好。
2021-04-19 22:57:42
这是这里最好的答案
2021-05-11 22:57:42

为了增加选定的答案,我一直在测试一个使用 jinja2 和 Flask 也可以工作的新选项:

@app.route('/')
def my_view():
    data = [1, 2, 3, 4, 5]
    return render_template('index.html', data=data)

模板:

<script>
    console.log( {{ data | tojson }} )
</script>

渲染模板的输出:

<script>
    console.log( [1, 2, 3, 4] )
</script>

safe可以添加,但也喜欢{{ data | tojson | safe }}以避免HTML逃跑,但它是工作,没有太多。

我可以向您推荐一种面向 javascript 的方法,它可以轻松地在您的项目中使用 javascript 文件。

在 jinja 模板文件中创建一个 javascript 部分,并将所有要在 javascript 文件中使用的变量放在一个 window 对象中:

开始.html

...
{% block scripts %}
<script type="text/javascript">
window.appConfig = {
    debug: {% if env == 'development' %}true{% else %}false{% endif %},
    facebook_app_id: {{ facebook_app_id }},
    accountkit_api_version: '{{ accountkit_api_version }}',
    csrf_token: '{{ csrf_token }}'
}
</script>
<script type="text/javascript" src="{{ url_for('static', filename='app.js') }}"></script>
{% endblock %}

Jinja 将替换值,我们的 appConfig 对象将可以从我们的其他脚本文件中访问:

应用程序.js

var AccountKit_OnInteractive = function(){
    AccountKit.init({
        appId: appConfig.facebook_app_id,
        debug: appConfig.debug,
        state: appConfig.csrf_token,
        version: appConfig.accountkit_api_version
    })
}

我用这种更易于管理和 seo 友好的方式将 javascript 代码从 html 文档中分离出来。

@MarkAmery 感谢您的澄清。我今天使用两种方法在浏览器中获取服务器变量。第一个与答案有点不同<input type="hidden" name="server_data" value="DATA">数据是 json 编码的,当然必须转义。第二个是做一个API请求,如果数据依赖于访问者的最好使API请求得到它。
2021-04-16 22:57:42
-1 因为,就像这里的大多数其他答案一样,如果您正在模板化的字符串变量包含某些字符或子字符串(如单引号或</script><script>alert("XSSed ur site!!1")</script>,这将中断您在最后一段中关于 SEO 受到网站是使用内联 JavaScript 还是外部脚本影响的说法在我看来也是可疑的。
2021-05-05 22:57:42