简体   繁体   English

通过 Django 将 Python 数据传递给 JavaScript

[英]Passing Python Data to JavaScript via Django

I'm using Django and Apache to serve webpages.我正在使用 Django 和 Apache 来提供网页。 My JavaScript code currently includes a data object with values to be displayed in various HTML widgets based on the user's selection from a menu of choices.我的 JavaScript 代码当前包含一个数据对象,该对象的值将根据用户从选项菜单中的选择显示在各种 HTML 小部件中。 I want to derive these data from a Python dictionary.我想从 Python 字典中导出这些数据。 I think I know how to embed the JavaScript code in the HTML, but how do I embed the data object in that script (on the fly) so the script's functions can use it?我想我知道如何在 HTML 中嵌入 JavaScript 代码,但是如何将数据对象嵌入该脚本(动态)以便脚本的函数可以使用它?

Put another way, I want to create a JavaScript object or array from a Python dictionary, then insert that object into the JavaScript code, and then insert that JavaScript code into the HTML.换句话说,我想从 Python 字典创建一个 JavaScript 对象或数组,然后将该对象插入到 JavaScript 代码中,然后将该 JavaScript 代码插入到 HTML 中。

I suppose this structure (eg, data embedded in variables in the JavaScript code) is suboptimal, but as a newbie I don't know the alternatives.我认为这种结构(例如,嵌入在 JavaScript 代码中的变量中的数据)是次优的,但作为新手,我不知道替代方案。 I've seen write-ups of Django serialization functions, but these don't help me until I can get the data into my JavaScript code in the first place.我看过有关 Django 序列化函数的文章,但是在我首先将数据放入 JavaScript 代码之前,这些对我没有帮助。

I'm not (yet) using a JavaScript library like jQuery.我还没有(还)使用像 jQuery 这样的 JavaScript 库。

nb see 2018 update at the bottom nb 请参阅底部的 2018 年更新

I recommend against putting much JavaScript in your Django templates - it tends to be hard to write and debug, particularly as your project expands.我建议不要在你的 Django 模板中放置太多 JavaScript - 它往往难以编写和调试,尤其是当你的项目扩展时。 Instead, try writing all of your JavaScript in a separate script file which your template loads and simply including just a JSON data object in the template.相反,尝试将所有 JavaScript 编写在模板加载的单独脚本文件中,并在模板中仅包含一个 JSON 数据对象。 This allows you to do things like run your entire JavaScript app through something like JSLint , minify it, etc. and you can test it with a static HTML file without any dependencies on your Django app.这允许你做一些事情,比如通过JSLint 之类的东西运行你的整个 JavaScript 应用程序,缩小它等等。你可以用静态 HTML 文件测试它,而不依赖于你的 Django 应用程序。 Using a library like simplejson also saves you the time spent writing tedious serialization code.使用像 simplejson 这样的库还可以节省您编写繁琐的序列化代码的时间。

If you aren't assuming that you're building an AJAX app this might simply be done like this:如果你不假设你正在构建一个 AJAX 应用程序,这可能只是这样完成:

In the view:在视图中:

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, …})

In the template:在模板中:

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

Note that the type of data matters: if my_data is a simple number or a string from a controlled source which doesn't contain HTML, such as a formatted date, no special handling is required.请注意,数据类型很重要:如果my_data是一个简单的数字或来自不包含 HTML 的受控源的字符串,例如格式化的日期,则不需要特殊处理。 If it's possible to have untrusted data provided by a user you will need to sanitize it using something like the escape or escapejs filters and ensure that your JavaScript handles the data safely to avoid cross-site scripting attacks.如果可能有用户提供的不受信任的数据,您需要使用诸如escapeescapejs过滤器之类的东西来清理它,并确保您的 JavaScript 安全地处理数据以避免跨站点脚本攻击。

As far as dates go, you might also want to think about how you pass dates around.就日期而言,您可能还想考虑如何传递日期。 I've almost always found it easiest to pass them as Unix timestamps:我几乎总是发现将它们作为 Unix 时间戳传递是最容易的:

In Django:在姜戈:

time_t = time.mktime(my_date.timetuple())

In JavaScript, assuming you've done something like time_t = {{ time_t }} with the results of the snippet above:在 JavaScript 中,假设您已经使用上述代码段的结果执行了类似time_t = {{ time_t }}的操作:

my_date = new Date();
my_date.setTime(time_t*1000);

Finally, pay attention to UTC - you'll want to have the Python and Django date functions exchange data in UTC to avoid embarrassing shifts from the user's local time.最后,请注意 UTC - 您需要让 Python 和 Django 日期函数以 UTC 交换数据,以避免与用户本地时间发生令人尴尬的变化。

EDIT : Note that the setTime in javascript is in millisecond whereas the output of time.mktime is seconds.编辑:请注意,javascript 中的 setTime 以毫秒为单位,而 time.mktime 的输出以秒为单位。 That's why we need to multiply by 1000这就是为什么我们需要乘以 1000

2018 Update: I still like JSON for complex values but in the intervening decade the HTML5 data API has attained near universal browser support and it's very convenient for passing simple (non-list/dict) values around, especially if you might want to have CSS rules apply based on those values and you don't care about unsupported versions of Internet Explorer. 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";
});

This is a neat way to expose data to CSS if you want to set the initial view state in your Django template and have it automatically update when JavaScript updates the data- attribute.如果您想在 Django 模板中设置初始视图状态并让它在 JavaScript 更新data-属性时自动更新,这是将数据公开给 CSS 的一种巧妙方法。 I use this for things like hiding a progress widget until the user selects something to process or to conditionally show/hide errors based on fetch outcomes or even something like displaying an active record count using CSS like #some-element::after { content: attr(data-active-transfers); }我使用它来隐藏进度小部件,直到用户选择要处理的内容或根据获取结果有条件地显示/隐藏错误,甚至使用 CSS 显示活动记录计数,如#some-element::after { content: attr(data-active-transfers); } #some-element::after { content: attr(data-active-transfers); } . #some-element::after { content: attr(data-active-transfers); } .

For anyone who might be having a problems with this, be sure you are rendering your json object under safe mode in the template.对于可能遇到此问题的任何人,请确保您在模板中以安全模式呈现 json 对象。 You can manually set this like this您可以像这样手动设置

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

As of mid-2018 the simplest approach is to use Python's JSON module, simplejson is now deprecated.截至 2018 年年中,最简单的方法是使用 Python 的 JSON 模块,现在已弃用 simplejson。 Beware, that as @wilblack mentions you need to prevent Django's autoescaping either using safe filter or autoescape tag with an off option.请注意,正如@wilblack 提到的,您需要使用safe过滤器或带有off选项的自动autoescape标签来防止 Django 的自动autoescape In both cases in the view you add the contents of the dictionary to the context在视图中的两种情况下,您都将字典的内容添加到上下文中

viewset.py视图集.py

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

and then in the template you add as @wilblack suggested:然后在模板中添加@wilblack 建议:

template.html模板.html

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

You can include <script> tags inside your .html templates, and then build your data structures however is convenient for you.您可以在 .html 模板中包含<script>标签,然后构建您的数据结构,但对您来说很方便。 The template language isn't only for HTML, it can also do Javascript object literals.模板语言不仅用于 HTML,它还可以用于 Javascript 对象文字。

And Paul is right: it might be best to use a json module to create a JSON string, then insert that string into the template. Paul 是对的:最好使用 json 模块创建一个 JSON 字符串,然后将该字符串插入到模板中。 That will handle the quoting issues best, and deal with deep structures with ease.这将最好地处理引用问题,并轻松处理深层结构。

It is suboptimal.这是次优的。 Have you considered passing your data as JSON using django's built in serializer for that?您是否考虑过使用 django 的内置序列化程序将数据作为 JSON 传递?

See the related response to this question .请参阅此问题的相关回复。 One option is to use jsonpickle to serialize between Python objects and JSON/Javascript objects.一种选择是使用jsonpickle在 Python 对象和 JSON/Javascript 对象之间进行序列化。 It wraps simplejson and handles things that are typically not accepted by simplejson.它包装 simplejson 并处理 simplejson 通常不接受的事情。

Putting Java Script embedded into Django template is rather always bad idea.把嵌入Django的模板Java脚本是相当总是坏主意。

Rather , because there are some exceptions from this rule.相反,因为这条规则有一些例外。

Everything depends on the your Java Script code site and functionality.一切都取决于您的 Java Script 代码站点和功能。

It is better to have seperately static files, like JS, but the problem is that every seperate file needs another connect/GET/request/response mechanism.最好有单独的静态文件,比如 JS,但问题是每个单独的文件都需要另一个连接/GET/请求/响应机制。 Sometimes for small one, two liners code os JS to put this into template, bun then use django templatetags mechanism - you can use is in other templates ;)有时对于小一,两行代码 os JS 将其放入模板中,然后使用 django templatetags 机制 - 您可以在其他模板中使用 is ;)

About objects - the same.关于对象- 相同。 If your site has AJAX construction/web2.0 like favour - you can achieve very good effect putting some count/math operation onto client side.如果您的站点具有 AJAX 构造/web2.0 之类的优势 - 您可以在客户端进行一些计数/数学运算,从而获得非常好的效果。 If objects are small - embedded into template, if large - response them in another connection to avoid hangind page for user.如果对象很小 - 嵌入到模板中,如果很大 - 在另一个连接中响应它们以避免用户挂起页面。

Fixing the security hole in the answers by @willblack and @Daniel_Kislyuk.修复@willblack 和@Daniel_Kislyuk 答案中的安全漏洞。

If the data is untrusted, you cannot just do如果数据不受信任,您不能只做

viewset.py视图集.py

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

template.html模板.html

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

because the data could be something like {"</script><script>alert(123);</script>":""} and forward slashes aren't escaped by default.因为数据可能类似于{"</script><script>alert(123);</script>":""}并且默认情况下不会转义正斜杠。 Clearly the escaping by json.dumps may not 100% match the escaping in Javascript, which is where the problems come from.很明显, json.dumps的转义可能不是 100% 匹配 Javascript 中的转义,这就是问题的来源。

Fixed solution固定解决方案

As far as I can tell, the following fixes the problem:据我所知,以下解决了问题:

<script>
   my_data = JSON.parse("{{ my_dictionary|escapejs }}");
</script>

If there are still issues, please post in the comments.如果还有问题,请在评论中留言。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM