通过Django将Python数据传递给JavaScript
我正在使用Django和Apache来提供网页服务。我的JavaScript代码里有一个数据对象,这个对象的值会根据用户从菜单中选择的内容显示在不同的HTML小部件上。我想把这些数据从一个Python字典中提取出来。我觉得我知道怎么把JavaScript代码嵌入到HTML里,但我不知道怎么把这个数据对象动态地嵌入到脚本中,以便脚本的功能可以使用它。
换句话说,我想从一个Python字典创建一个JavaScript对象或数组,然后把这个对象放到JavaScript代码里,再把这段JavaScript代码放到HTML中。
我想这个结构(比如,把数据嵌入到JavaScript代码里的变量中)可能不是最优的,但作为一个新手,我不知道还有什么其他的选择。我见过关于Django序列化函数的介绍,但这些在我还不能把数据放到JavaScript代码里的时候对我没有帮助。
我现在还没有使用像jQuery这样的JavaScript库。
8 个回答
截至2018年中,最简单的方法是使用Python的JSON模块,现在simplejson已经不再推荐使用了。需要注意的是,正如@wilblack提到的,你需要防止Django自动转义,可以使用safe
过滤器或者autoescape
标签并设置为off
选项。在这两种情况下,你都需要在视图中将字典的内容添加到上下文中。
viewset.py
import json
def get_context_data(self, **kwargs):
context['my_dictionary'] = json.dumps(self.object.mydict)
然后在模板中,你可以按照@wilblack的建议添加:
template.html
<script>
my_data = {{ my_dictionary|safe }};
</script>
安全警告:
json.dumps
不会转义斜杠:一个攻击示例是{'</script><script>alert(123);</script>': ''}
。这个问题和其他回答中提到的一样。添加了另一个回答,希望能解决这个问题。
如果你在这方面遇到问题,确保你在模板中以安全模式渲染你的json对象。你可以像这样手动设置:
<script type="text/javascript">
data_from_django = {{ my_data|safe }};
widget.init(data_from_django);
</script>
注意:请查看底部的2018年更新
我不建议在Django模板中放太多JavaScript代码,因为这样写起来和调试都比较麻烦,尤其是当你的项目变得越来越大时。相反,建议把所有的JavaScript代码写在一个单独的脚本文件里,然后在模板中加载这个文件,只在模板中包含一个JSON数据对象。这样做的好处是,你可以通过像JSLint这样的工具来检查你的JavaScript代码,压缩它等等,而且你可以用一个静态的HTML文件来测试,而不需要依赖你的Django应用。使用像simplejson这样的库也能节省你写繁琐的序列化代码的时间。
如果你不打算做一个AJAX应用,可以这样简单处理:
在视图中:
from django.utils import simplejson
def view(request, …):
js_data = simplejson.dumps(my_dict)
…
render_template_to_response("my_template.html", {"my_data": js_data, …})
在模板中:
<script type="text/javascript">
data_from_django = {{ my_data }};
widget.init(data_from_django);
</script>
注意数据的类型很重要:如果my_data
是一个简单的数字或者来自受控来源的字符串(比如格式化的日期),而且不包含HTML,那么就不需要特别处理。如果数据可能来自用户并且不可信,你需要使用像escape或escapejs这样的过滤器来清理数据,并确保你的JavaScript安全地处理这些数据,以避免跨站脚本攻击。
关于日期,你也需要考虑如何传递日期。我发现把日期作为Unix时间戳传递是最简单的:
在Django中:
time_t = time.mktime(my_date.timetuple())
在JavaScript中,假设你已经用类似time_t = {{ time_t }}
的方式处理了上面的结果:
my_date = new Date();
my_date.setTime(time_t*1000);
最后,注意UTC时间——你需要确保Python和Django的日期函数在交换数据时使用UTC,以避免用户本地时间的尴尬偏差。
编辑:注意,JavaScript中的setTime是以毫秒为单位,而time.mktime的输出是以秒为单位。这就是为什么我们需要乘以1000。
2018年更新:我仍然喜欢使用JSON来处理复杂值,但在这十年间,HTML5数据API已经获得了几乎所有浏览器的支持,这对于传递简单(非列表/字典)值非常方便,特别是当你希望根据这些值应用CSS规则时,而不在乎不支持的Internet Explorer版本。
<div id="my-widget" data-view-mode="tabular">…</div>
let myWidget = document.getElementById("my-widget");
console.log(myWidget.dataset.viewMode); // Prints tabular
somethingElse.addEventListener('click', evt => {
myWidget.dataset.viewMode = "list";
});
这是一种很好的方法,可以将数据暴露给CSS,如果你想在Django模板中设置初始视图状态,并在JavaScript更新data-
属性时自动更新。我用这个来处理一些事情,比如在用户选择处理的内容之前隐藏进度小部件,或者根据获取结果有条件地显示/隐藏错误,甚至像使用CSS显示活跃记录计数,像这样#some-element::after { content: attr(data-active-transfers); }
。