Reputation: 310
I am using django v1.5.*, I am going to render the a variable named "foobar" which is a json obj and including unicode string.
def home( request ): import json foo = {"name": u"赞我们一下"} bar = json.dumps( foo ) return render_to_response( 'myapp/home.html', { "foobar": bar, }, context_instance=RequestContext(request) )
And in my template, I encode the json obj in javascript and then append to the div, it can display the expected string:
foobar=JSON.encode('{{foobar|safe}}'); $("#foobar").html(foobar.name);`
then I can get the 赞一下我们
on my web page.
But I found that if I use the variable directly:
<div id="foobar">{{ foobar }}</div>
it will display the unicode string as byte string:
{ "name":"\u8d5e\u4e00\u4e0b\u6211\u4eec" }
Even if I using the {{foobar|safe}}
then nothing change.
Now, I want to ask why this happend or is anything wrong of me? What should I do if I do want to using the variable directly as {{ foobar }}
?
Upvotes: 3
Views: 14040
Reputation: 536339
<div id="foobar">{{ foobar }}</div>
will display the unicode string as byte string:
{ "name":"\u8d5e\u4e00\u4e0b\u6211\u4eec" }
This is displaying the whole object in its JSON-encoded form. Whilst you could use json.dumps(..., ensure_ascii=False)
to keep non-ASCII characters like the Chinese in their raw form rather than encoded into ASCII-safe-JSON, this would still give you {"name": "赞一下我们"}
which is probably not what you want.
If you only want the name
property of the object included in the div, you will need to pass the item foo['name']
to your template - or the whole of foo
and have the template include {{foo.name}}
- rather than including the whole encoded JSON object from foobar
.
foobar=JSON.encode('{{foobar|safe}}');
There is no method encode
on the ECMA standard JSON
object, so I don't know what that's doing.
The JSON-encoding method is JSON.stringify
. But you don't want to encode here - you already have a string with a JSON representation of an object here because you encoded it above at the server side in the call to json.dumps
. Probably you meant:
var foobar = JSON.parse('{{foobar|safe}}');
which would work for your particular value of name
, but not necessarily in general. For example if the name was a\nb
then you would get:
JSON.parse('{"name": "a\nb"}')
in which case the \n
would get parsed by the JavaScript interpreter as being a newline character in the string, and so the string parsed as JSON would be:
{"name": "a
b"}
which is a syntax error. There are also security issues here - contrary to the |safe
assertion, this is not safe. If the name
property contained the string </script
then you would end up with a prematurely-ended script element, which can lead to cross-site scripting attacks:
<script>
var foobar = JSON.parse('</script>
<script>alert();//attacker code here');</script>
You could attempt to get around these by doing a second layer of JS string literal encoding on the JSON at the server side, and manually replace <
with \u003C
. But probably it's best to take a different approach - generating executable code on the fly is fraught with problems in general, especially when you have multiple different syntaxes in the mix (Python/Django-template, HTML and JS/JSON).
If you write data to the document (for example in a data-
attribute) rather than the script then you can keep the normal and safe template behaviour of HTML-escaping. Then the JS side can read the data out of the DOM. For example:
<div id="foobar" data-foobar="{{ foobar }}">
...
var foobar = $('#foobar').data('foobar');
(jQuery's data
method implicitly does the JSON.parse
for you.)
This helps with separation of concerns, getting JS out into external scripts, and potentially eventually using Content-Security-Policy.
Upvotes: 4
Reputation: 481
bar = json.dumps(foo, ensure_ascii=False)
will result in bar
being a unicode
object; without ensure_ascii=False, bar
is a str
.
Django's smart_text method might also be useful for conversions.
Upvotes: 7
Reputation: 798526
You need to reference the element within the dict, without JSON-encoding it.
return render_to_response(..., {..., 'foo': foo}, ...)
...
{{ foo.name }}
Upvotes: 1