通过Django将Python数据传递给JavaScript

60 投票
8 回答
93047 浏览
提问于 2025-04-15 14:25

我正在使用Django和Apache来提供网页服务。我的JavaScript代码里有一个数据对象,这个对象的值会根据用户从菜单中选择的内容显示在不同的HTML小部件上。我想把这些数据从一个Python字典中提取出来。我觉得我知道怎么把JavaScript代码嵌入到HTML里,但我不知道怎么把这个数据对象动态地嵌入到脚本中,以便脚本的功能可以使用它。

换句话说,我想从一个Python字典创建一个JavaScript对象或数组,然后把这个对象放到JavaScript代码里,再把这段JavaScript代码放到HTML中。

我想这个结构(比如,把数据嵌入到JavaScript代码里的变量中)可能不是最优的,但作为一个新手,我不知道还有什么其他的选择。我见过关于Django序列化函数的介绍,但这些在我还不能把数据放到JavaScript代码里的时候对我没有帮助。

我现在还没有使用像jQuery这样的JavaScript库。

8 个回答

6

截至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>': ''}。这个问题和其他回答中提到的一样。添加了另一个回答,希望能解决这个问题。

45

如果你在这方面遇到问题,确保你在模板中以安全模式渲染你的json对象。你可以像这样手动设置:

<script type="text/javascript">
    data_from_django = {{ my_data|safe }};
    widget.init(data_from_django);
</script>
82

注意:请查看底部的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,那么就不需要特别处理。如果数据可能来自用户并且不可信,你需要使用像escapeescapejs这样的过滤器来清理数据,并确保你的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); }

撰写回答