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 [ > Object:ID <, > Object:ID <,... ]
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 dict
s, 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>&'}
, 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 >
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 %}
精彩评论