如何通过Jinja2将列表从Python传递给JavaScript
假设我有一个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 个回答
你可以使用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的版本中是没有的。
我在使用Flask的时候遇到过类似的问题,但我并没有使用JSON。我只是传递了一个列表 letters = ['a','b','c']
,然后用 render_template('show_entries.html', letters=letters)
这个方法把它传给了模板。
var letters = {{ letters|safe }}
在我的JavaScript代码中,我设置了这个内容。Jinja2会把 {{ letters }}
替换成 ['a','b','c']
,这样JavaScript就把它当作一个字符串数组来处理了。
要把一些上下文数据传递给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(跨站脚本攻击)漏洞的风险!