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

99 投票
7 回答
168600 浏览
提问于 2025-04-17 18:34

假设我有一个Python变量:

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

我把它传给Jinja来渲染HTML,同时我在JavaScript中有一个叫做 somefunction(variable) 的函数。我想把 list_of_items 中的每个项目都传过去。我试过类似这样的做法:

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

我想问,是否可以把一个列表从Python传到JavaScript,还是说我应该一个一个地在循环中传递列表里的每个项目?我该怎么做呢?

7 个回答

25

你可以使用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,可以这样写:

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

不要在你的Python代码中使用json.dumps来将变量转换成JSON格式,然后把生成的JSON文本传递给模板。这会导致某些字符串值输出不正确,并且如果你试图编码用户提供的值,会让你面临XSS攻击的风险。这是因为Python内置的json.dumps不会转义像<>这样的字符(这些字符需要转义才能安全地嵌入到内联<script>中,具体可以参考这个链接),或者单引号(这些字符需要转义才能安全地嵌入到单引号的HTML属性中)。

如果你在使用Flask,注意Flask会注入一个自定义的tojson过滤器,而不是使用Jinja的版本。不过,上面提到的内容依然适用。这两个版本的行为几乎是一样的;Flask的版本只是允许一些特定于应用的配置,而这些在Jinja的版本中是没有的。

40

我在使用Flask的时候遇到过类似的问题,但我并没有使用JSON。我只是传递了一个列表 letters = ['a','b','c'],然后用 render_template('show_entries.html', letters=letters) 这个方法把它传给了模板。

var letters = {{ letters|safe }}

在我的JavaScript代码中,我设置了这个内容。Jinja2会把 {{ letters }} 替换成 ['a','b','c'],这样JavaScript就把它当作一个字符串数组来处理了。

139

要把一些上下文数据传递给JavaScript代码,你需要把这些数据转换成JavaScript能理解的格式,通常是JSON格式。同时,你还需要用safe这个Jinja过滤器来标记这些数据,以防止它们被转义成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函数),你需要把列表中的每个项目单独转换成JSON格式。你的代码看起来会像这样:

视图

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(跨站脚本攻击)漏洞的风险!

撰写回答