I wrote this little function for writing out HTML tags:
def html_tag(tag, content=None, close=True, attrs={}):
lst = ['<',tag]
for key, val in attrs.iteritems():
lst.append(' %s="%s"' % (key, escape_html(val)))
if close:
if content is None: lst.append(' />')
else: lst.extend(['>', content, '</', tag, '>'])
else:
lst.append('>')
return mark_safe(''.join(lst))
Which worked great, but then I read this article on efficient string concatenation (I know it doesn't really matter for this, but I wanted consistency) and decided to update my script:
def html_tag(tag, body=None, close=True, attrs={}):
s = StringIO()
s.write('<%s'%tag)
for key, val in attrs.iteritems():
s.write(' %s="%s"' % (key, escape_html(val)))
if close:
if body is None: s.write(' />')
else: s.write('>%s</%s>' % (body, tag))
else:
s.write('>')
return mark_safe(s.getvalue())
But now my HTML get escaped when I try to render it from my template. Everything else is exactly the same. It works properly if I replace the last line with return mark_safe(unicode(s.getvalue()))
. I checked the return type of s.getvalue()
. It should be a str
, just like the first function, so why is this failing??
Also fails with SafeString(s.getvalue())
but succeeds with SafeUnicode(s.getvalue())
.
I'd also like to point out that I used return mark_safe(s.getvalue())
in a different function with no odd behavior.
The "call stack" looks like this:
class Input(Widget):
def render(self):
return html_tag('input', attrs={'type':self.itype, 'id':self.id,
'name':self.name, 'value':self.value, 'class':self.itype})
class Field:
def __unicode__(self):
return mark_safe(self.widget.render())
And then {{myfield}}
is in the template. So it does get mark_safed
'd twice, which I thought might have been the problem, but I tried removing that too..... I really have no idea what's causing t开发者_如何学JAVAhis, but it's not too hard to work around, so I guess I won't fret about it.
The render
method of your widget is called by the BoundField.__unicode__
function, which returns SafeString instead (of a subclass of unicode
.)
Many places in Django (e.g. django.template.VariableNode.render
) will actually call force_unicode
on the field instance itself. This will have the effect of doing unicode(instance.__unicode__())
, so even though instance.__unicode__()
returns a SafeString
object, it will become a regular unicode
object.
To illustrate, have a look at the snippet below:
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
class Foo(object):
def __unicode__(self):
return mark_safe("foo")
foo = Foo()
print "foo =", type(foo)
ufoo = unicode(foo)
print "ufoo =", type(ufoo)
forced_foo = force_unicode(foo)
print "forced_foo =", type(forced_foo)
bar = mark_safe("bar")
print "bar =", type(bar)
forced_bar = force_unicode(bar)
print "forced_bar =", type(forced_bar)
Output:
foo = <class 'testt.Foo'>
ufoo = <type 'unicode'>
forced_foo = <type 'unicode'>
bar = <class 'django.utils.safestring.SafeString'>
forced_bar = <class 'django.utils.safestring.SafeUnicode'>
精彩评论