将对象从 Django 传递到 Javascript DOM

Posted

技术标签:

【中文标题】将对象从 Django 传递到 Javascript DOM【英文标题】:Passing objects from Django to Javascript DOM 【发布时间】:2011-11-02 04:47:33 【问题描述】:

我正在尝试将一个查询集从 Django 传递到一个带有 javascript 的模板。

我尝试了不同的方法来解决这个问题:

1.正常方法 - 由于命名法 [ &gt Object:ID &lt, &gt Object:ID &lt,... ]

,Javascript 在尝试解析对象时变得一团糟

Django 视图

django_list = list(Some_Object.objects.all())

模板 html + JS

<script type="text/javascript" >
    var js_list = django_list;
</script>

2。 JSON 方法 - Django 无法将对象列表转换为 json 字符串 不是 JSON 可序列化的

Django 视图

django_list = list(Some_Object.objects.all())
json_list = simplejson.dumps(django_list)

模板 HTML + JS

<script type="text/javascript" >
    var js_list = json_list;
</script>

所以,我需要一些帮助:)

任何人有任何建议/解决方案?

谢谢!

【问题讨论】:

【参考方案1】:

Django 查询集are serializable by JSON。某些字段类型(例如日期,显然)不能在 is 序列化。日期对象的解决方法发布在another question on JSON and Python。

我建议直接在 JavaScript 本身中创建字典。给定这样的模型:

class Article(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    content = models.TextField()

class Author(models.Model):
    article = models.ForeignKey("Article", related_name="authors")
    first_name=models.CharField(max_length=100)
    last_name=models.CharField(max_length=100)

我会在模板中做这样的事情:

<script type="text/javascript">
    var articles = [
    % for article in article_list %
        % if not forloop.first %,% endif %
        
            title: " article.title ",
            slug: " article.slug ",
            content: " article.content ",
            authors: [
            % for author in article.authors.all %
                % if not forloop.first %,% endif %
                
                    first_name: " author.first_name ",
                    last_name: " author.last_name ",
                
            % endfor %
            ]
        
    % endfor %
    ]
</script>

如果您可能对这个问题的措辞有点糟糕,并且 计划在 &lt;script&gt; 标记中插入代码并且实际上出于某种原因需要 JSON,我' d 简单的在视图中做一个循环,创建一个dicts 的列表,JSON 序列化没有问题,JavaScript 理解也没有问题。

【讨论】:

【参考方案2】:

您的问题是,通常情况下,您的要求没有明确说明。您究竟希望 JSON 是什么样子?你说你想“序列化查询集”,但是用什么格式?你想要来自每个模型实例的所有字段、一个选择,还是只是 unicode 表示?当您回答了这个问题后,您就会知道如何解决您的问题。

例如,一种方法可能是使用values queryset 方法为每个实例输出一个字段字典,并将其序列化(您需要先将其转换为列表):

data = SomeObject.objects.values('field1', 'field2', 'field3')
serialized_data = simplejson.dumps(list(data))

【讨论】:

嗨@Daniel,我想要一个对象列表,就像查询集一样。从 Javascript 的角度来看,这可能是一个对象数组。 叹息。您希望对象包含什么?【参考方案3】:

您必须将字符串标记为安全,以确保它不会被转义。

在我的一个项目中,我这样使用它:

# app/templatetag/jsonify.py
from django import template
from django.utils.safestring import mark_safe
import json

register = template.Library()

@register.filter
def jsonify(list):
    return mark_safe(json.dumps(list))

在模板中

% load jsonify %
<script type="text/javascript" >
    var js_list =  python_list|jsonify|escapejs ;
</script>

但您可能更喜欢只添加 mark_safe 或在模板中使用 |safe 以避免所有 &amp;gt; 内容

如果问题在于处理复杂的 python 对象,您可能必须像这样执行处理程序:JSON datetime between Python and JavaScript

【讨论】:

这很容易受到 XSS 攻击。例如,尝试以下 Python 列表:['&lt;/script&gt;&lt;script&gt;alert("hello")//']. 你是对的,在这种情况下,除非模板呈现 .js 文件,否则我们应该添加我忘记的“|escapejs”,我编辑了,谢谢 Blender【参考方案4】:

好的,我找到了解决方案!

主要是因为没有引用结果。当 Javascript 试图解析对象时,这未被识别为字符串。

所以,第一步是:

var js_list = django_list; 

改为:

var js_list = "django_list";

在这之后我意识到 Django 正在转义字符,所以我不得不像这样替换它们:

 var myJSONList = (("json_list").replace(/&(l|g|quo)t;/g, function(a,b)
                return 
                    l   : '<',
                    g   : '>',
                    quo : '"'
                [b];
            ));

 myData = JSON.parse( myJSONList );

注意:我试图避免使用 Django 转义字符

var js_list = "json_list|safe"; 

但这不起作用,因为它与引号混淆。

最后我找到了一种方法来避免在将其发送到 Javascript 之前将其转换为 JSON 的后端逻辑:

var myDjangoList = (("django_list |safe").replace(/&(l|g|quo)t;/g, function(a,b)
            return 
                l   : '<',
                g   : '>',
                quo : '"'
            [b];
        ));

myDjangoList = myDjangoList.replace(/u'/g, '\'')
myDjangoList = myDjangoList.replace(/'/g, '\"')

myData = JSON.parse( myDjangoList );

我确信这可以改进,我把这个告诉你;)

感谢您的回答

希望对其他人有所帮助!

【讨论】:

这个答案不是一个优雅的方法。试试***.com/questions/10502135/… 事实证明,该链接问题的答案也不是很好。 Json.dumps 不再起作用,需要使用序列化程序。现在,这是一个很好的方法,但是对于传递给模板的模型的简单 values_list 函数,此答案中的方法有效。【参考方案5】:

Django 为您在此处尝试执行的场景提供内置帮助。它是这样的:

您的视图中有一个 python 序列、列表、字典等,我们称之为py_object。一种方法是在将其传递给渲染引擎之前对其进行 jsonify。

from django.shortcuts import render_to_response
import json  

以后就这样用了……

render_to_response('mypage.html','js_object':json.dumps(py_object))

在您的模板中,然后使用 safe 过滤器将已经 json 化的对象从 python 导入到 javascript 中,像这样...

data =  js_object|safe 

我希望这应该能解决你的问题。

【讨论】:

【参考方案6】:

编辑:请不要使用此方法,请参阅@agconti 的回答。

使用 escapejs 过滤器:https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#escapejs

转储列表示例:

var foo = [% for x in y %' x|escapejs ',% endfor %]

【讨论】:

如果我有一个嵌套列表怎么办?那我该如何转储呢?使用上面的代码,我只得到第一级的项目。任何帮助将不胜感激。 @HarshMehta 看到下面我的答案:使用 DjangoJSONEncoder。【参考方案7】:

同样的问题,“更好”(最近)答案:Django Queryset to dict for use in json

vashishtha-jogi回复:

更好的方法是使用 DjangoJSONEncoder。它支持小数。

import json
from django.core.serializers.json import DjangoJSONEncoder

prices = Price.objects.filter(product=product).values_list('price','valid_from')

prices_json = json.dumps(list(prices), cls=DjangoJSONEncoder)

非常易于使用。无需为转换个人而跳槽 字段浮动。

更新:将答案更改为使用内置 json 而不是 simplejson。

这个答案在我的谷歌搜索中经常出现,并且有很多观点,更新它似乎是个好主意,让其他人免于挖掘 SO。假设Django 1.5

【讨论】:

除了.values_list(),也可以使用.values() 来获取所有内容 prices_json被创建并传递给DOM后,你也可以通过var prices_data= prices_json|safe ;将其内容赋给一个Javascript变量【参考方案8】:

您可以在 Django 中使用safe 和escapejs 内置过滤器的组合。

var json_string = unescape(json_list | safe | escapejs);
var json_data = JSON.parse(json_string);

【讨论】:

安全 |逃跑是另一种在其他时间起作用的可能性【参考方案9】:

综合答案(我的环境:Django 2.0)

在views.py中

import json
data= []
// fil the list
context['mydata'] = json.dumps('data':data)

在模板中

  <script type="text/javascript">
      var mydataString = "mydata|escapejs";
      console.log(JSON.parse(mydataString));
  </script>

【讨论】:

【参考方案10】:

从 Django 2.1 开始,就有了json-script template tag。来自文档:

json_script

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

参数:标签的 HTML “id”。

例如:

 value|json_script:"hello-data"  

如果 value 是字典 'hello': 'world',输出将是:

<script id="hello-data" type="application/json">
"hello": "world"
</script>

可以在 JavaScript 中访问结果数据 像这样:

var value = JSON.parse(document.getElementById('hello-data').textContent); 

通过转义字符“”和“&”可以缓解 XSS 攻击。为了 例如,如果值为'hello': 'world&lt;/script&gt;&amp;amp;',则输出为:

<script id="hello-data" type="application/json">
    "hello": "world\\u003C/script\\u003E\\u0026amp;"
</script> 

这是兼容的 具有禁止页内脚本的严格内容安全策略 执行。它还保持被动数据之间的清晰分离 和可执行代码。

【讨论】:

【参考方案11】:

让我发送整个 QuerySet(同时保留字段名称;发送 object 而不是 list)。我用了以下

    # views.py        
    units = Unit.objects.all()
    units_serialized = serializers.serialize('json', units)
    context['units'] = units_serialized

只需在模板中使用safe 标记

    # template.html
    <script>
            console.log(units|safe);
    </script>

【讨论】:

【参考方案12】:

django 2.1 的注意事项

我在 django 文档中发现这有点令人困惑,所以简单地解释一下简单的方法。

我们通常会像这样使用它

 my_info 

或根据我们的需要循环它。但是如果我们使用下面的过滤器,

json_script 

我们可以安全地将这个值输出为 JSON

 my_info|json_script:"my-info" 

我们的数据以 JSON 格式添加,包装在脚本标签中,我们可以看到数据。我们现在可以通过在 JavaScript 中查找来使用这个值,如下所示:

info = JSON.parse(document.getElementById('my-info').textContent);

【讨论】:

【参考方案13】:

要么;

使用 django_list 读取对象,然后删除不需要的字符

或者做;

 django_list | safe

【讨论】:

【参考方案14】:

还要注意确保从 Django 正确输出 JSON 数据,否则前端的所有试验都将浪费时间。在我的情况下,我不能使用 JsonResponse 作为渲染函数的一部分,所以我做了以下事情:

    def view(self, request):

        data = []
        machine_data = list(Machine.objects.filter(location__isnull=False).values_list('serial', 'location', 'customer__name'))
        data.append(
            "locations": machine_data,
        )

        return render(request, 'admin/company/device/map.html', 
            "machines": data
        )

在前端:

% block content %

     machines_with_location|json_script:"machineLocationData" 

    <div id="content-main">

        <h1>Title</h1>

        <script type="text/javascript">

            const locationDataFromJson = JSON.parse(document.getElementById('machineLocationData').textContent);

        </script>

    </div>

% endblock %

【讨论】:

以上是关于将对象从 Django 传递到 Javascript DOM的主要内容,如果未能解决你的问题,请参考以下文章

将 'dict' 对象从 Django 模板传递到 Angular 控制器 - 避免 jsonify 和解析

Django 将选定对象从表单传递到另一个表单

Django将对象从视图传递到下一个进行处理

django+javascrip的疑问一

将数据从 Django 传递到 D3

将字符串列表从 Django 传递到 Javascript