Django:mark_safe的奇怪行为?

3

我为编写HTML标签编写了这个小函数:

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

这样做很好,但后来我读了这篇文章:高效的字符串连接(我知道这对此并不重要,但我想保持一致性),于是决定更新我的脚本:

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())

但是,当我尝试从我的模板中呈现它时,我的HTML被转义了。其他方面完全相同。如果我将最后一行替换为return mark_safe(unicode(s.getvalue())),它就可以正常工作。我检查了s.getvalue()的返回类型。它应该是一个str,就像第一个函数一样,那么为什么会失败呢??
使用SafeString(s.getvalue())也会失败,但使用SafeUnicode(s.getvalue())则成功。
我还想指出,我在另一个函数中使用了return mark_safe(s.getvalue()),没有任何奇怪的行为。
"调用堆栈"看起来像这样:
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())

然后{{myfield}}就在模板中了。所以它确实被mark_safed处理了两次,我认为这可能是问题所在,但我也试过把它移除.....我真的不知道是什么原因导致的,但解决起来并不太难,所以我想我不会为此感到烦恼。


实际上,我无法复现该行为,请问您正在使用哪个Django版本? - Wolph
在这种情况下,我认为还有其他原因导致了这个HTML的转义。也许某些代码支持SafeUnicode但不支持SafeString?我找不到任何可能出错的代码。 - Wolph
1个回答

7
你的小部件的render方法是由BoundField.__unicode__函数调用的,该函数返回SafeString而不是unicode子类。在Django的许多地方(例如django.template.VariableNode.render),实际上会对字段实例本身调用force_unicode。这将导致执行unicode(instance.__unicode__()),因此即使instance.__unicode__()返回一个SafeString对象,它也会变成一个常规的unicode对象。
为了说明这一点,请查看下面的代码片段:
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)

输出:

foo = <class 'testt.Foo'>
ufoo = <type 'unicode'>
forced_foo = <type 'unicode'>
bar = <class 'django.utils.safestring.SafeString'>
forced_bar = <class 'django.utils.safestring.SafeUnicode'>

这帮助我解决了一个令人困惑的mark_safe失败问题,谢谢。简而言之,在调用该方法之前,请确保您要'mark_safe'的字符串是unicode。 - James Murty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接