开发者

Passing objects from Django to Javascript DOM

I'm trying to pass a Query Set from Django to a template with javascript.

I've tried different approaches to solve this:

1. Normal Approach - Javascript gets all messed up with trying to parse the object because of the nomenclature [ &gt Object:ID &lt, &gt Object:ID &lt,... ]

Django View开发者_JS百科

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

Template HTML + JS

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

2. JSON Approach - Django fails on converting the object list to a json string is not JSON serializable

Django View

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

Template HTML + JS

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

So, I need some help here :)

Any one has any suggestion / solution?

Thanks!


Same Question, "Better"(more recent) answer: Django Queryset to dict for use in json

Answer by vashishtha-jogi:

A better approach is to use DjangoJSONEncoder. It has support for Decimal.

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)

Very easy to use. No jumping through hoops for converting individual fields to float.

Update : Changed the answer to use builtin json instead of simplejson.

This is answer came up so often in my google searches and has so many views, that it seems like a good idea to update it and save anyone else from digging through SO. Assumes Django 1.5.


Ok, I found the solution!

Mostly it was because of not quoting the results. When Javascript was trying to parse the object this wasn't recognized as string.

So, first step is:

var js_list = {{django_list}}; 

changed to:

var js_list = "{{django_list}}";

After this I realized that Django was escaping characters so I had to replace them like this:

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

 myData = JSON.parse( myJSONList );

Note: I tried to avoid escaping characters from Django using this:

var js_list = "{{json_list|safe}}"; 

But this doesn't work because it gets confused with the quotes.

Finally I found a way to avoid the logic on the backend of converting to JSON before sending it to Javascript:

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 );

I'm sure this can be improved, I let this to you ;)

Thanks for your answers

Hope it helps to someone else!


Django querysets are serializable by JSON. Some field types (such as date, apparently), can't be serialized at is. A workaround for date objects is posted in another question on JSON and Python.

I would recommend creating dictionaries directly in the JavaScript itself. Given models like this:

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)

I'd do something like this in the template:

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

If you maybe worded the question a little poorly and aren't planning on inserting code in a <script> tag and actually need JSON for some reason, I'd simply do a loop in the view and create a list of dicts, which JSON has no problem serializing, and JavaScript no problem in understanding.


EDIT: please don't use this method, see @agconti's answer.

Use the escapejs filter: https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#escapejs

Example of dumping a list:

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


Since Django 2.1 there is the json-script template tag. From the docs:

json_script

Safely outputs a Python object as JSON, wrapped in a tag, ready for use with JavaScript.

Argument: HTML “id” of the tag.

For example:

{{ value|json_script:"hello-data" }} 

If value is the dictionary {'hello': 'world'}, the output will be:

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

The resulting data can be accessed in JavaScript like this:

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

XSS attacks are mitigated by escaping the characters “<”, “>” and “&”. For example if value is {'hello': 'world</script>&amp;'}, the output is:

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

This is compatible with a strict Content Security Policy that prohibits in-page script execution. It also maintains a clean separation between passive data and executable code.


You can use the combination of safe and escapejs built-in filter in Django.

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


Your problem is that, as so often, your requirements are under-specified. What exactly do you want the JSON to look like? You say you want to "serialize the queryset", but in what format? Do you want all the fields from each model instance, a selection, or just the unicode representation? When you've answered that question, you'll know how to solve your problem.

One approach, for example, might be to use the values queryset method to output a dictionary of fields for each instance, and serialize that (you need to convert it to a list first):

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


You have to mark the string as safe to be sure it's not escaped.

in one of my project I use it like this:

# 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))

and in the template

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

but you may prefer to just add mark_safe or use |safe in the template to avoid all &gt; stuff

If the problem is for handling complex python object you may have to do your handler like this: JSON datetime between Python and JavaScript


Django offers in-built help for the very scenario you are trying to do here. It goes something like this:

You have a python sequence, list, dictionary, etc in your view, let's call it py_object. One approach is to jsonify it before passing it to the rendering engine.

from django.shortcuts import render_to_response
import json  

Then later on use like this...

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

In you template, then use the safe filter to import the already jsonized object from python into javascript, like this...

data = {{ js_object|safe }}

That should solve your problem i hope.


either;

read object using {{ django_list }} and then remove unwanted characters

or do;

{{ django_list | safe}}


Consolidated answer (my env: Django 2.0)

In views.py

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

In template

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


For me to send the whole QuerySet (while preserving the fields names; sending object not list). I used the following

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

and just use safe tag in the template

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


NOTE for django 2.1

i found this a little confusing on django documentation so simply explaining a little bit easy way.

we would normally use this like

{{ my_info }}

or loop over it depending on what we needed. But if we use the following filter,

json_script 

we can safely output this value as JSON

{{ my_info|json_script:"my-info" }}

Our data has been added as JSON, wrapped in a script tag, and we can see the data. We can now use this value by looking it up in JavaScript like so:

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


Be careful on also making sure that you output JSON data correctly from Django, otherwise all trials on the frontend side will be a waste of time. In my case I could not use JsonResponse as part of the render function so I did the following:

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

And on the frontend:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜