Unicode string display on Django template

13,072

Solution 1

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.

Solution 2

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

Solution 3

You need to reference the element within the dict, without JSON-encoding it.

return render_to_response(..., {..., 'foo': foo}, ...)

...

{{ foo.name }}
Share:
13,072
nevesly
Author by

nevesly

Updated on June 04, 2022

Comments

  • nevesly
    nevesly almost 2 years

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