Reputation: 646
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
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
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