GordonsBeard
GordonsBeard

Reputation: 646

Cannot get django template tag to return unicode

I'm writing an app that will track a series of matches between 2 players. I am using Django's User model and extending it with my own UserProfile.

I store usernames in User as their steamID (ex: 76561197965801299) and then look up their steam username on login, and update UserProfile.

Instead of looking at 76561197965801299, I want to look at a username, and on one page, I want to decorate this username with more goodies, so I wrote a template tag.

Problem:

I cannot seem to print unicode data from my template tag.

Actual Error:

'ascii' codec can't encode character u'\u260e' in position 16: ordinal not in range(128)

Normally Django doesn't bother me with unicode issues (for example: I can see this unicode object in the admin pages no problem) but I've never tried applying a template tag, so there is obviously something I'm doing wrong here.

template/ladder/match_game_listing.html

{{ match.challengee|steam_name }}

The match.challengee in this case is 76561197971597000.

ladder/templatetags/ladder_filters.py

from django import template
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.utils.html import mark_safe
from cafe.models import UserProfile

register = template.Library()

@register.filter()
def steam_name(name):
    try:
        user_obj = User.objects.get(username=name)
        user_prof = UserProfile.objects.get(user_id=user_obj.id)

        url = user_prof.url
        handle = unicode(user_prof.handle)
        avatar = user_prof.avatar

        steam_string = "<a href='{0}' alt='{1}\'s profile'><img src='{2}' alt='{1}\'s avatar' >{1}</a>".format(url, handle, avatar)

        return mark_safe(steam_string)

    # Non-steam entities can exist, ignore
    except ObjectDoesNotExist:
        return name

When I go to view this in the browser, I get the aforementioned error:

UnicodeEncodeError at /ladder/dota2/ 'ascii' codec can't encode character u'\u260e' in position 16: ordinal not in range(128)

With a helpful hint of:

Unicode error hint

The string that could not be encoded/decoded was: oose ☎

I've tried browsing the Django docs numerous times, and I have tried playing with force_text() to no avail, but as I'm a little unclear on why this isn't working, I might just be missing the relevant section. This template tag works in cases where the name does not have unicode.

Upvotes: 0

Views: 3852

Answers (2)

bobince
bobince

Reputation: 536715

steam_string = "<a href='{0}' alt='{1}\'s profile'><img src='{2}' alt='{1}\'s avatar' >{1}</a>".format(url, handle, avatar)

The .format() method won't promote a format string from bytes to unicode just because there are unicode strings in the formatting parameters. The output of str.format is always str and the output of unicode.format is always unicode. In this way it differs from the old % operator, where str % unicode -> unicode.

Since you want Unicode output, your format string needs to be Unicode (u"" string) too. Also,

return mark_safe(steam_string)

Why are you marking this safe? It isn't. Without HTML-escaping, any HTML-special characters in the formatted parameters can cause an HTML-injection vulnerability leading to XSS attacks. Also your apostrophes are mis-escaped:

"alt='{1}\'s profile'"

\' is a Python string literal escape; you will be returning the string:

alt='someone's profile'

which means you'll never see the 's profile suffix as it isn't part of the attribute value.

Suggest:

from django.utils.html import escape
...
return mark_safe(
    u'<a href="{0}" alt="{1}\'s profile">'
        u'<img src="{2}" alt="{1}\'s avatar">{1}'
    u'</a>'
).format(escape(url), escape(handle), escape(avatar))

Upvotes: 5

ronniemagatti
ronniemagatti

Reputation: 1867

I might be thinking this very simply and in a wrong way but, maybe if you just tell django its a unicode by putting the letter 'u' before the string.

something like:

unicodeString = "ís"
string = u"this -> %s my unicode string" % unicodeString

I'm sorry if i didn't understand your question at all. (i can't comment so i posted an answer)

Got that from reading this: https://docs.djangoproject.com/en/dev/ref/unicode/

Upvotes: 3

Related Questions