【问题标题】:Passing objects from Django to Javascript DOM将对象从 Django 传递到 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>

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

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

谢谢!

【问题讨论】:

    标签: javascript python django json dom


    【解决方案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 );
      

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

      感谢您的回答

      希望对其他人有所帮助!

      【讨论】:

      • 这个答案不是一个优雅的方法。试试stackoverflow.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 中使用safeescapejs 内置过滤器的组合。

        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 %}
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2019-10-28
                    • 1970-01-01
                    • 2023-03-25
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-11-25
                    • 2021-01-14
                    • 1970-01-01
                    相关资源
                    最近更新 更多