ChatGPT解决这个技术问题 Extra ChatGPT

Django 模板变量和 Javascript

当我使用 Django 模板渲染器渲染页面时,我可以传入一个包含各种值的字典变量,以使用 {{ myVar }} 在页面中操作它们。

有没有办法在 Javascript 中访问相同的变量(也许使用 DOM,我不知道 Django 如何使变量可访问)?我希望能够根据传入的变量中包含的值使用 AJAX 查找来查找详细信息。


M
Mr. Polywhirl

{{variable}} 直接替换为 HTML。做一个视图源;它不是“变量”或类似的东西。它只是呈现的文本。

话虽如此,您可以将这种替换放入您的 JavaScript 中。

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}";
</script>

这为您提供了“动态”javascript。


请注意,尽管根据 this solution,这很容易受到注入攻击
@Casebash:对于这种情况 escapejs 过滤器存在:escapejs('<>') -> u'\\u003C\\u003E'
只是补充一下以供参考:如果“someDjangoVariable”恰好是 JSON,请务必使用 {{ someDjangoVariable|safe }} 删除“”
如果 javascript 写在不同的文件中怎么办?
10 年后,Django 为此引入了内置模板过滤器:docs.djangoproject.com/en/2.1/ref/templates/builtins/…
Y
Yaroslav Admin

注意 请查看票 #17419,了解有关将类似标签添加到 Django 核心中的讨论以及使用此模板标签和用户生成的数据可能引入的 XSS 漏洞。来自 amacneil 的 Comment 讨论了票证中提出的大部分问题。

我认为最灵活、最方便的方法是为要在 JS 代码中使用的变量定义模板过滤器。这使您可以确保您的数据被正确转义,并且您可以将其用于复杂的数据结构,例如 dictlist。这就是为什么我写这个答案,尽管有很多赞成的答案。

这是模板过滤器的示例:

// myapp/templatetags/js.py

from django.utils.safestring import mark_safe
from django.template import Library

import json


register = Library()


@register.filter(is_safe=True)
def js(obj):
    return mark_safe(json.dumps(obj))

此模板过滤器将变量转换为 JSON 字符串。你可以像这样使用它:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>

这很好,因为它只允许将一些 Django 模板输入变量复制到 Javascript,并且服务器端代码不需要知道 Javascript 必须使用哪些数据结构,因此在呈现 Django 模板之前将其转换为 JSON。要么使用它,要么总是将所有 Django 变量复制到 Javascript。
@JorgeOrpinel 不,不一样。 safe 仅将值标记为安全,没有适当的转换和转义。
然后如何在 django 模板中显示变量?
@Sandi 当我发布它时,通常在单独的 JS 文件中有一个小部件并在页面源代码中对其进行初始化。因此,假设您在 JS 文件中声明 function myWidget(config) { /* implementation */ },然后在使用 myWidget({{ pythonConfig | js }}) 的某些页面上使用它。但是您不能在 JS 文件中使用它(如您所见),因此它有其局限性。
b
bosco-

对我有用的解决方案是使用模板中的隐藏输入字段

<input type="hidden" id="myVar" name="variable" value="{{ variable }}">

然后以这种方式在javascript中获取值,

var myVar = document.getElementById("myVar").value;

不过要小心。根据您使用变量/表单的方式,用户可以输入他们想要的任何内容。
您可能还想将输入字段设置为只读(请参阅此链接 w3schools.com/tags/att_input_readonly.asp
如果它不会改变数据库或不会发送到数据库查询,那就没问题了。 @AndyL
伙计们……用户无论如何都可以做他们想做的事。如今,浏览器通过功能齐全的 DOM 检查器和调试工具使这一切变得如此简单。故事的寓意:在服务器上进行所有数据验证。
请谁能告诉我你将如何访问外部 JavaScript 文件中的该变量?
P
Paolo

从 Django 2.1 开始,专门为此用例引入了一个新的内置模板标记:json_script

https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script

新标签将安全地序列化模板值并防止 XSS。

Django 文档摘录:

将 Python 对象安全地输出为 JSON,包装在标签中,可与 JavaScript 一起使用。


J
Josh

使用内置模板标签 json_script 从 Django 2.1+ 实现了一种非常简单的方法。一个简单的例子是:

在模板中声明变量:

{{ variable|json_script:'name' }}

然后在您的 <script> Javascript 中调用该变量:

var js_variable = JSON.parse(document.getElementById('name').textContent);

对于更复杂的变量,如“用户”,使用 Django 的内置序列化程序,您可能会收到“用户类型的对象不是 JSON 可序列化”之类的错误。在这种情况下,您可以使用 Django Rest Framework 来允许更复杂的变量。


这实际上可以在没有任何复杂的 3rd 方脚本的情况下工作。
I
Insomniak

这是我正在做的非常容易的事情:我为我的模板修改了我的 base.html 文件并将其放在底部:

{% if DJdata %}
    <script type="text/javascript">
        (function () {window.DJdata = {{DJdata|safe}};})();
    </script>
{% endif %}

然后当我想在 javascript 文件中使用变量时,我创建一个 DJdata 字典并通过 json 将其添加到上下文中:context['DJdata'] = json.dumps(DJdata)

希望能帮助到你!


不要使用它,它容易受到脚本注入 (XSS) 的攻击。
s
shacker

对于以文本形式存储在 Django 字段中的 JavaScript 对象,它需要再次成为动态插入到页面脚本中的 JavaScript 对象,您需要同时使用 escapejsJSON.parse()

var CropOpts = JSON.parse("{{ profile.last_crop_coords|escapejs }}");

Django 的 escapejs 正确处理引用,JSON.parse() 将字符串转换回 JS 对象。


这实际上应该是答案。这完美地工作,没有任何奇怪的手动报价转义。
S
Shahriar.M

新的 docs 表示使用 {{ mydata|json_script:"mydata" }} 来防止代码注入。

给出了一个很好的例子 here

{{ mydata|json_script:"mydata" }}
<script>
    const mydata = JSON.parse(document.getElementById('mydata').textContent);
</script>

总体而言,2021 年的最佳答案。 Jon Saks 提供了相同的内容,但没有在答案中包含实际代码,仅包含指向文档的链接(链接中断、更改、移动......)
J
JJ.

对于字典,最好先编码为 JSON。您可以使用 simplejson.dumps(),或者如果您想从 App Engine 中的数据模型进行转换,您可以使用 GQLEncoder 库中的 encode()。


请注意,我相信从 django 1.7 开始,“simplejson”变成了“json”。
D
Daniel Kislyuk

请注意,如果要将变量传递给外部 .js 脚本,则需要在脚本标记之前加上另一个声明全局变量的脚本标记。

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

<script type="text/javascript" src="{% static "scripts/my_script.js" %}"></script>

data 在视图中像往常一样在 get_context_data 中定义

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context

全局声明变量的部分实际上很有帮助。
如果您将数据从上述模板渲染到 js 变量中,则必须渲染到同一页面(使它们成为全局),其他代码转到单独的 js 文件。在服务器端必须有效数据,因为用户可以从浏览器更改。
C
Conquistador

我面临着类似的问题,S.Lott 建议的答案为我工作。

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}"
</script>

但是,我想在这里指出主要的实现限制。如果您打算将您的 javascript 代码放在不同的文件中,并将该文件包含在您的模板中。这行不通。

这仅在您的主模板和 javascript 代码位于同一文件中时才有效。可能 django 团队可以解决这个限制。


我们怎样才能克服这一点?
为了克服这个问题,您可以像在包含静态 Javascript 文件之前显示的示例一样放置全局 Javascript 变量。您的静态 Javascript 文件将可以通过这种方式访问所有全局变量。
不要使用它,它容易受到脚本注入 (XSS) 的攻击。
他们可以在服务器端验证数据。
S
Shoval Sadde

我也一直在为此苦苦挣扎。从表面上看,上述解决方案似乎应该有效。但是,django 架构要求每个 html 文件都有自己的渲染变量(即,{{contact}} 渲染到 contact.html,而 {{posts}} 渲染到例如 index.html 等等)。另一方面,<script> 标记出现在 contact.htmlindex.html 继承的 base.html 中的 {%endblock%} 之后。这基本上意味着任何解决方案,包括

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

必然会失败,因为变量和脚本不能共存于同一个文件中。

我最终想出并为我工作的简单解决方案是简单地用带有 id 的标签包装变量,然后在 js 文件中引用它,如下所示:

// index.html
<div id="myvar">{{ myVar }}</div>

接着:

// somecode.js
var someVar = document.getElementById("myvar").innerHTML;

并且像往常一样在 base.html 中包含 <script src="static/js/somecode.js"></script>。当然,这只是关于获取内容。关于安全性,只需遵循其他答案即可。


您可能希望使用 .textContent 而不是 .innerHTML,否则获得 HTML 编码的实体也将成为 JS 变量的一部分。但即便如此,它也可能不会以 1:1 的比例复制(我不确定)。
一个澄清。我使用类似的方法来捕获变量中的字段值(使用在表单中动态创建的 ID),并且它正在工作(但仅适用于 ONE formset 行)。我无法解决的是从表单集字段的所有行中捕获值,这些字段是手动填充的(即在使用 for 循环的 html 表中)。正如您将看到的,只有最后一个 formset 行的变量被传递给变量,并且在 for 循环通过 formset 行时,前面的值被后面的值覆盖。有没有办法解决这个问题?
D
Devesh Pradhan

我发现我们可以将 Django 变量传递给 javascript 函数,如下所示:-

<button type="button" onclick="myJavascriptFunction('{{ my_django_variable }}')"></button>
<script>
    myJavascriptFunction(djangoVariable){
       alert(djangoVariable);
    }
</script>

h
henrry

我在 Django 2.1 中使用这种方式并为我工作,这种方式是安全的 (reference)

Django方面:

def age(request):
    mydata = {'age':12}
    return render(request, 'test.html', context={"mydata_json": json.dumps(mydata)})

html端:

<script type='text/javascript'>
     const  mydata = {{ mydata_json|safe }};
console.log(mydata)
 </script>

我认为您误解了您提供的参考文章。它清楚地表明使用 'json.dumps' 和 ' |安全'在一起也是一种脆弱的方式。阅读“另一种易受攻击的方式”标题下方的段落。
“另一种易受攻击的方式”段在哪里?
D
Daniel Muñoz

您可以组装整个脚本,其中您的数组变量在字符串中声明,如下所示,

视图.py

    aaa = [41, 56, 25, 48, 72, 34, 12]
    prueba = "<script>var data2 =["
    for a in aaa:
        aa = str(a)
        prueba = prueba + "'" + aa + "',"
    prueba = prueba + "];</script>"

这将生成一个字符串,如下所示

prueba = "<script>var data2 =['41','56','25','48','72','34','12'];</script>"

获得此字符串后,您必须将其发送到模板

视图.py

return render(request, 'example.html', {"prueba": prueba})

在模板中,您收到它并以文学方式将其解释为 htm 代码,就在您需要它的 javascript 代码之前,例如

模板

{{ prueba|safe  }}

下面是代码的其余部分,请记住,示例中使用的变量是 data2

<script>
 console.log(data2);
</script>

这样你就可以保留数据的类型,在这种情况下是一种安排


仅当您确定 aaa 将仅包含数字时才使用此选项,否则可能会出现 XSS(脚本注入)。
S
Sourabh SInha

在 Javascript 中有两件事对我有用:

'{{context_variable|escapejs }}'

和其他:在views.py中

from json import dumps as jdumps

def func(request):
    context={'message': jdumps('hello there')}
    return render(request,'index.html',context)

在html中:

{{ message|safe }}