This is more of a general python question, but it becomes a little more complicated in the context of Django.
I have a template, like this, simplified:
<span class="unit">miles</span>
I'm replacing an element with jquery and ajax:
$.getJSON('/getunit/', function(data){
$('#unitHolder').html(data.unit_html);
});
Which goes to a view function to retrieve json data (more data than just this template). So I wanted to serve it up as json, and not just a string. So, the relevant code is this:
...
context = { 'qs' : queryset }
data['unit'] = render_to_string('map/unit.html', context)
data = str(data).replace('\'','"') #json wants double quotes
return HttpResponse(data, mimetype="application/json")
This works for all of our other data, but not the template, because it has double quotes in it, that are not escaped. My question is, how do you escape a string in python to be used in a json format? Note that render_to_string() renders the string in unicode, thus u"<span>...</span>".
I've tried
import json
data['unit'] = json.dumps(render_to_string('map/unit.html', context))
But that gives me "unit": ""<span class=\\"unit\\">miles</span>"".
Also:
data['unit'] = str(render_to_string('map/unit.html', context)).replace('"','\"')
and:
data['unit'] = str(render_to_string('map/unit.html', context)).replace('"','\\"')
But neither escape the double quotes properly.
I hadn't tried json.dumps until I came into this problem. Previously I was just converting python dictionaries to strings and then replacing single quotes with double quotes. And for most of the data we've been passing to the client, it rendered correct JSON format. Now that I've tried json.dumps here in this problem, I realized that I don't need to convert dictionaries with str or replace anything. I can render the script as follows:
...
context = { 'qs' : queryset }
data['unit'] = render_to_string('map/unit.html', context)
import json # I'll import this earlier
data = json.dumps(data)
return HttpResponse(data, mimetype="application/json")
This works with all the data I'm passing to JSON format, so that works perfectly fine, and that's how I should have been doing it all along.
Related
I have a JSON file hosted locally in my Django directory. It is fetched from that file to a view in views.py, where it is read in like so:
def Stops(request):
json_data = open(finders.find('JSON/myjson.json'))
data1 = json.load(json_data) # deserialises it
data2 = json.dumps(data1) # json formatted string
json_data.close()
return JsonResponse(data2, safe=False)
Using JsonResponse without (safe=False) returns the following error:
TypeError: In order to allow non-dict objects to be serialized set the safe parameter to False.
Similarly, using json.loads(json_data.read()) instead of json.load gives this error:
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
This is confusing to me - I have validated the JSON using an online validator. When the JSON is sent to the frontend with safe=False, the resulting object that arrives is a string, even after calling .json() on it in javascript like so:
fetch("/json").then(response => {
return response.json();
}).then(data => {
console.log("data ", data); <---- This logs a string to console
...
However going another step and calling JSON.parse() on the string converts the object to a JSON object that I can use as intended
data = JSON.parse(data);
console.log("jsonData", data); <---- This logs a JSON object to console
But this solution doesn't strike me as a complete one.
At this point I believe the most likely thing is that there is something wrong with the source JSON - (in the file character encoding?) Either that or json.dumps() is not doing what I think it should, or I am not understanding the Django API's JSONresponse function in a way I'm not aware of...
I've reached the limit of my knowledge on this subject. If you have any wisdom to impart, I would really appreciate it.
EDIT: As in the answer below by Abdul, I was reformatting the JSON into a string with the json.dumps(data1) line
Working code looks like:
def Stops(request):
json_data = open(finders.find('JSON/myjson.json'))
data = json.load(json_data) # deserialises it
json_data.close()
return JsonResponse(data, safe=False) # pass the python object here
Let's see the following lines of your code:
json_data = open(finders.find('JSON/myjson.json'))
data1 = json.load(json_data) # deserialises it
data2 = json.dumps(data1) # json formatted string
You open a file and get a file pointer in json_data, parse it's content and get a python object in data1 and then turn it back into a JSON string and store it into data2. Somewhat redundant right? Next you pass this JSON string to JsonResponse which will further try to serialize it into JSON!! Meaning you then get a string inside a string in JSON.
Try the following code instead:
def Stops(request):
json_data = open(finders.find('JSON/myjson.json'))
data = json.load(json_data) # deserialises it
json_data.close()
return JsonResponse(data, safe=False) # pass the python object here
Note: function names in python should ideally be in snake_case not PascalCase, hence instead of Stops you should use stops. See
PEP 8 -- Style Guide for Python
Code
I have a python function/method that takes in a student and will show profile... i realized i need to return context as a json string. how do i do that?
context["student"] = db.query_dict(student_profile_sql.format(student_id=self.kwargs["student_id"])
)[0]
appear(self.request, "Show profile", {
"student_name": context["student"]["first_name...
})
return context // i need to return context as json string how can i do that?
How can i return context as a json string?
Import the json library:
import json
Then use json.dumps:
return json.dumps(context)
From the Python documentation:
json.dumps(obj, ...)
Serialize obj to a JSON formatted str
I have a simple flask function that renders a template with a valid GeoJSON string:
#app.route('/json', methods=['POST'])
def json():
polygon = Polygon([[[0,1],[1,0],[0,0],[0,1]]])
return render_template('json.html',string=polygon)
In my json.html file, I am attempting to render this GeoJSON with OpenLayers:
function init(){
map = new OpenLayers.Map( 'map' );
layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{layers: 'basic'} );
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
var fc = {{string}}; //Here is the JSON string
var geojson_format = new OpenLayers.Format.GeoJSON();
var vector_layer = new OpenLayers.Layer.Vector();
map.addLayer(vector_layer);
vector_layer.addFeatures(geojson_format.read(fc));
But this fails and the " characters become '. I have tried string formatting as seen in this question, but it didn't work.
EDIT:
I did forget to dump my json to an actual string, I'm using the geojson library so adding the function
dumps(polygon)
takes care of that, however I still can't parse the GeoJSON in OpenLayers, even though it is a valid string according to geojsonlint.com
This is the Javascript code to create a variable from the string sent from flask:
var geoJson = '{{string}}';
And here's what it looks like in the source page:
'{"type": "Polygon", "coordinates": [[[22.739485934746977, 39.26596659794341], [22.73902517923571, 39.266115931275074], [22.738329551588276, 39.26493626464484], [22.738796023230854, 39.26477459496181], [22.739485934746977, 39.26596659794341]]]}';
I am still having a problem rendering the quote characters.
Look like you use shapely which has http://toblerity.org/shapely/shapely.geometry.html#shapely.geometry.mapping method to create GeoJSON-like object.
To render json use tojson filter which safe (see safe filter) for latest flask versions, because jinja2 in flask by default escape all dangerous symbols to protect XSS.
i am developing one of my site with the python django where i have been using angularjs in one of my page where i have given the user option to search (specific request). Here is my model..
class Request(models.Model):
description = models.TextField(blank=True,null=True)
category = models.ForeignKey(Category)
sub_category = models.ForeignKey(SubCategory)
In my views I am returning through the following code:
def some(code, x):
exec code
return x
def search_request(request):
page = request.GET.get('page')
term = request.GET.get('term')
i = 0
terms = "x = Request.objects.filter("
for t in term.split(" "):
i=i+1
if(len(term.split(" "))>i):
terms = terms +"Q(name__icontains='"+t+"')|"
else:
terms = terms +"Q(name__icontains='"+t+"'))"
junk = compile(terms,'<string>', 'exec')
spit = Request.objects.filter(name__icontains=term)
requests = some(junk,spit)
output = HttpResponse()
output['values']=[{'requests':r,'category':r.category,'subcategory':r.sub_category} for r in requests]
return JSONResponse(output['values'])
In my HTML code when I return using AngularJS:
$scope.search = function(){
$scope.results = $http.get("{% url 'search-requests-show' %}?term="+$scope.term).then(
function(result){
return result.data;
}
);
}
The result on the HTML Output comes as in {[{results}]}:
"[{'category': <Category: The New Category>, 'requests': <Request: Need a Table>, 'subcategory': <SubCategory: Testsdfsdfsad>}]"
The problem is that I am not being able to access using results.category because the output is in "string", so the ng-repeat="result in results" brings the result as
[ { ' c a .....
I am probably doing something wrong in view. If anybody has any suggestion then please answer.
JSONResponse is probably using standard python json encoder, which doesn't really encode the object for you, instead it outputs a string representation (repr) of it, hence the <Category: The New Category> output.
You might need to use some external serializer class to handle django objects, like:
http://web.archive.org/web/20120414135953/http://www.traddicts.org/webdevelopment/flexible-and-simple-json-serialization-for-django
If not, you should then normalize object into simple python types inside the view (dict, list, string.., the kind that json module has no problem encoding). So instead doing:
'category':r.category
you could do:
'category': {'name': r.category.name}
Also as a sidenote: using exec is super bad idea. Don't use it in production!
You should return json strings for Angular:
import json
def resource_view(request):
# something to do here
return HttpResponse(json.dumps(your_dictionary))
for better usage i recommend djangorestframework.
Off Topic:
$http.get("{% url 'search-requests-show' %}?term="+$scope.term);
you can pass 'param' object:
$http.get("{% url 'search-requests-show' %}", {param : {term:$scope.term}});
I have a dict like this:
data = {"data":"http://abc/def"}
when I call json.dumps(data) I get this:
'{"data":"http://abc/def"}'
but I want this:
'{"data":"http:\/\/abc\/def"}'
because I use jquery to parse json but seems like it don't understand unescaped solidus, or is there any way to make jquery understand?
UPDATE
For example, here is my json data
{"data": ["http://abc.com/aaaaaaaa/bbbbbbbbb/cccccccccc/xyz.mp3"]}
Here is my success function
function showResult(result) {
$.each(result.data, function(i, item){
link = $('<a>').attr('href', item).text(item)
$("#result").append('<br>')
$("#result").append(link);
});
}
The result should be a hyperlink to
http://abc.com/aaaaaaaa/bbbbbbbbb/cccccccccc/xyz.mp3
But I got a hyperlink to
http://abc.com/aaaaaaaa/bbbbbbbbb/cccccccccc/xyz.mp3
If replace all '/' by '\/', everything is fine
Normally you don't escape forward slashes in JSON, but if you are certain this is your problem you can simply do this:
s = json.dumps(data)
s = s.replace("/", "\\/")