通过 Django 将 Python 数据传递给 JavaScript
Posted
技术标签:
【中文标题】通过 Django 将 Python 数据传递给 JavaScript【英文标题】:Passing Python Data to JavaScript via Django 【发布时间】:2010-11-29 13:55:52 【问题描述】:我正在使用 Django 和 Apache 来提供网页服务。我的 javascript 代码当前包含一个数据对象,其值将根据用户从选项菜单中的选择显示在各种 html 小部件中。我想从 Python 字典中获取这些数据。我想我知道如何在 HTML 中嵌入 JavaScript 代码,但是如何(动态)将数据对象嵌入到该脚本中,以便脚本的函数可以使用它?
换句话说,我想从 Python 字典创建一个 JavaScript 对象或数组,然后将该对象插入到 JavaScript 代码中,然后将该 JavaScript 代码插入到 HTML 中。
我认为这种结构(例如,嵌入在 JavaScript 代码中的变量中的数据)不是最理想的,但作为一个新手,我不知道替代方案。我看过 Django 序列化函数的文章,但在我首先将数据放入我的 JavaScript 代码之前,这些文章对我没有帮助。
我(还没有)使用像 jQuery 这样的 JavaScript 库。
【问题讨论】:
Django Template Variables and Javascript的可能重复 【参考方案1】:n.b.请参阅底部的 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 安全地处理数据以避免cross-site scripting 攻击。
就日期而言,您可能还想考虑如何传递日期。我几乎总是发现将它们作为 Unix 时间戳传递是最容易的:
在 Django 中:
time_t = time.mktime(my_date.timetuple())
在 JavaScript 中,假设您使用上述 sn-p 的结果完成了类似 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 来处理复杂值,但在这十年间the HTML5 data API 已经达到了near universal browser support 并且传递简单(非列表/字典)值非常方便,特别是如果你可能想要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 公开数据的好方法。我将其用于隐藏进度小部件等事情,直到用户选择要处理的内容或根据获取结果有条件地显示/隐藏错误,甚至使用诸如 #some-element::after content: attr(data-active-transfers);
之类的 CSS 显示活动记录计数。
【讨论】:
谢谢。我计划将 javascript 作为媒体文件提供服务,类似于 css 样式表。这似乎是比在 js 中嵌入数据更好的解决方案,尽管我必须学习一些 JSON,并编写一些服务器端代码来处理数据请求。 听起来是个好主意——我真正喜欢这种方法的一件事是编写一个返回 JSON 的 Django 视图很简单(如果你经常这样做,请查看 django-piston 以创建 REST API),并且很容易以这种方式测试孤立的部分。 我认为 my_data|safe 是正确的,而不是 my_data ? 不,不,不,不!浏览器在 Javascript 之前解析 HTML。如果my_dict
有一个包含</script>
的字符串,它将跳出脚本标签。
@macrocosme:是的——见我之前的评论。除非您使用来自已知安全来源的数据,或者已验证为整数、日期等纯安全类型的数据。否则,您需要制定一种策略来清理任何使用 |safe
呈现的数据。【参考方案2】:
对于可能对此有疑问的任何人,请确保您在模板中以安全模式呈现您的 json 对象。您可以像这样手动设置
<script type="text/javascript">
data_from_django = my_data|safe ;
widget.init(data_from_django);
</script>
【讨论】:
注意:数据可能包含</script>
序列。
在下面添加了一个答案,希望能修复这个漏洞。【参考方案3】:
截至 2018 年年中,最简单的方法是使用 Python 的 JSON 模块,simplejson 现在已被弃用。请注意,正如@wilblack 提到的,您需要使用safe
过滤器或带有off
选项的autoescape
标记来防止Django 自动转义。在这两种情况下,您都将字典的内容添加到上下文中
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>': ''
。与其他答案相同的问题。添加了另一个答案,希望能修复它。
【讨论】:
【参考方案4】:您可以在 .html 模板中包含 <script>
标记,然后构建您的数据结构,但对您来说很方便。模板语言不仅适用于 HTML,它还可以处理 Javascript 对象字面量。
Paul 是对的:最好使用 json 模块来创建 JSON 字符串,然后将该字符串插入到模板中。这将最好地处理引用问题,并轻松处理深层结构。
【讨论】:
【参考方案5】:这是次优的。您是否考虑过使用 django 的内置序列化程序将数据作为 JSON 传递?
【讨论】:
我想在涉足首字母缩略词之前我会学习一些基础知识。但似乎我可以学习一些 JSON,或者从基本元素构建一个解决方案,一旦我学会了一些 JSON,我就会放弃。【参考方案6】:查看this question的相关回复。一种选择是使用jsonpickle 在 Python 对象和 JSON/Javascript 对象之间进行序列化。它包装了 simplejson 并处理了 simplejson 通常不接受的事情。
【讨论】:
【参考方案7】:将 Java Script 嵌入到 Django 模板中相当始终是个坏主意。
相当,因为这条规则有一些例外。
一切都取决于您的 Java Script 代码站点和功能。
最好有单独的静态文件,比如 JS,但问题是每个单独的文件都需要另一个连接/GET/request/response 机制。有时对于小的一个,两个衬垫编码 os JS 将其放入模板中,然后使用 django 模板标签机制 - 你可以在其他模板中使用 is ;)
关于对象 - 相同。如果您的网站有 AJAX 构建/web2.0 之类的青睐 - 您可以在客户端进行一些计数/数学运算,以获得非常好的效果。如果对象很小 - 嵌入到模板中,如果很大 - 在另一个连接中响应它们以避免用户挂起页面。
【讨论】:
是的,你已经在 django 中内置了 json ;) 忘记 xml :P【参考方案8】:修复@willblack 和@Daniel_Kislyuk 答案中的安全漏洞。
如果数据不可信,您不能就这样做
viewset.py
def get_context_data(self, **kwargs):
context['my_dictionary'] = json.dumps(self.object.mydict)
template.html
<script>
my_data = my_dictionary|safe ;
</script>
因为数据可能类似于
"</script><script>alert(123);</script>":""
默认情况下不会转义正斜杠。显然json.dumps
的转义可能不是 100% 匹配 Javascript 中的转义,这就是问题所在。
固定解决方案
据我所知,以下解决了问题:
<script>
my_data = JSON.parse(" my_dictionary|escapejs ");
</script>
如果还有问题,请在 cmets 发帖。
【讨论】:
以上是关于通过 Django 将 Python 数据传递给 JavaScript的主要内容,如果未能解决你的问题,请参考以下文章