通过 Django 将 Python 数据传递给 JavaScript

IT技术 javascript python django
2021-01-12 17:57:02

我正在使用 Django 和 Apache 来提供网页。我的 JavaScript 代码当前包含一个数据对象,该对象的值将根据用户从选项菜单中的选择显示在各种 HTML 小部件中。我想从 Python 字典中导出这些数据。我想我知道如何将 JavaScript 代码嵌入到 HTML 中,但是如何将数据对象嵌入该脚本(动态)以便脚本的函数可以使用它?

换句话说,我想从 Python 字典创建一个 JavaScript 对象或数组,然后将该对象插入到 JavaScript 代码中,然后将该 JavaScript 代码插入到 HTML 中。

我认为这种结构(例如,嵌入在 JavaScript 代码中的变量中的数据)是次优的,但作为新手我不知道替代方案。我看过有关 Django 序列化函数的文章,但在我首先将数据放入 JavaScript 代码之前,这些对我没有帮助。

我还没有(还)使用像 jQuery 这样的 JavaScript 库。

6个回答

nb 请参阅底部的 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";
});

如果您想在 Django 模板中设置初始视图状态并让它在 JavaScript 更新data-属性时自动更新,这是将数据公开给 CSS 的一种巧妙方法我将它用于诸如隐藏进度小部件之类的事情,直到用户选择要处理的内容或根据获取结果有条件地显示/隐藏错误,甚至诸如使用 CSS 之类的显示活动记录计数之类的事情#some-element::after { content: attr(data-active-transfers); }

不不不不!浏览器在 Javascript 之前解析 HTML。如果my_dict有一个包含 的字符串</script>,它将脱离脚本标签。
2021-03-17 17:57:02
谢谢。我计划将 javascript 作为媒体文件提供,类似于 css 样式表。这似乎是比在 js 中嵌入数据更好的解决方案,尽管我必须学习一些 JSON,并编写一些服务器端代码来处理数据请求。
2021-03-18 17:57:02
我认为 {{ my_data|safe }} 是正确的而不是 {{ my_data }} 吗?
2021-03-28 17:57:02
听起来是个好主意 - 我真正喜欢这种方法的一件事是编写一个返回 JSON 的 Django 视图是微不足道的(如果你经常这样做,请查看 django-piston 以创建 REST API)并且它很容易测试这样孤立的部分。
2021-03-30 17:57:02
@macrocosme:是的——见我之前的评论。除非您使用的是来自已知安全来源的数据或已验证为纯安全类型(如整数、日期等)的数据,否则您需要有一个策略来清理任何使用|safe.
2021-04-11 17:57:02

对于可能遇到此问题的任何人,请确保您在模板中以安全模式呈现您的 json 对象。您可以像这样手动设置

<script type="text/javascript">
    data_from_django = {{ my_data|safe }};
    widget.init(data_from_django);
</script>
在下面添加了一个答案,希望能修复这个漏洞。
2021-03-18 17:57:02
注意:数据可能包含一个</script>序列。
2021-03-23 17:57:02

截至 2018 年年中,最简单的方法是使用 Python 的 JSON module,现在已弃用 simplejson。请注意,正如@wilblack 提到的,您需要使用带有选项的safe过滤器或autoescape标记来防止 Django 的自动转义off在视图中的两种情况下,您都将字典的内容添加到上下文中

视图集.py

import json
 def get_context_data(self, **kwargs):
    context['my_dictionary'] = json.dumps(self.object.mydict)

然后在模板中添加@wilblack 建议:

模板.html

<script>
    my_data = {{ my_dictionary|safe }};
</script>

安全警告json.dumps不逃避正斜杠:攻击是{'</script><script>alert(123);</script>': ''}与其他答案中的问题相同。添加了另一个答案,希望能修复它。

您可以<script>在 .html 模板中包含标签,然后构建您的数据结构,但对您来说很方便。模板语言不仅用于 HTML,它还可以用于 Javascript 对象文字。

Paul 是对的:最好使用 json module创建一个 JSON 字符串,然后将该字符串插入到模板中。这将最好地处理引用问题,并轻松处理深层结构。

这是次优的。您是否考虑过使用 django 的内置序列化程序将数据作为 JSON 传递?

我想在深入研究首字母缩略词之前我会学习一些基础知识。但似乎我可以学习一些 JSON,或者从基本元素构建一个解决方案,一旦我学习了一些 JSON,我就会废弃这些解决方案。
2021-04-10 17:57:02