常在河边走, 湿鞋是顺理成章的事情. 这次遇到的情况简报如下
# encoding=utf-8
import jinja2
import datetime
now = datetime.datetime.utcnow()
print jinja2.Template(u'''当前月份 {{ date.strftime('%Y 年 %m 月') }}''').render(date=now)
Traceback (most recent call last):
File "test.py", line 8, in <module>
print jinja2.Template(u'''当前月份 {{ date.strftime('%Y 年 %m 月') }}''').render(date=now)
File "/usr/local/lib/python2.7/dist-packages/Jinja2-2.6-py2.7.egg/jinja2/environment.py", line 894, in render
return self.environment.handle_exception(exc_info, True)
File "<template>", line 1, in top-level template code
UnicodeEncodeError: 'ascii' codec can't encode character u'\u5e74' in position 3: ordinal not in range(128)
一番尝试并寻找之后, 首先发现第一个问题,
datetime.datetime.strftime
函数接受奇怪的字符串会出问题, 比如先来试试看这个now = datetime.datetime.utcnow()
print now.strftime('%Y 年 %m 月')
now = datetime.datetime.utcnow()
print now.strftime(u'%Y 年 %m 月')
u
的 unicode
类型怎么说也坑太深了的感觉吧. (别扯什么 Python str
与 unicode
类型异与同 21 天从入门到精通什么的, 别的语言都没这问题 Python 确有, 这应该就是 Python 的问题了吧.) 在这种情况下如果要用 unicode
还得转一道now = datetime.datetime.utcnow()
print now.strftime(u'%Y 年 %m 月'.encode('utf-8'))
unicode
类型, 所以加一下 encode
.这看起来简直像是不明来历的野生 API, 连自家的
unicode
都不支持么. 可是注意 Jinja2 模板字符串里面那个格式化字符串并没有加任何前缀. 而貌似 Jinja2 也觉得 Python unicode
是个坑, 因此并不需要放字符串前缀, 相反放了还出错 (u'string'
会被认为是变量 u
与字符串 'string'
放一起中间没加二元运算符).搞了这么久问题还是没进展, 所以得采用其它方式更深入地调教一下. 那么, 把格式化函数外面套一层, 然后把套在外面的一层也搅进 Jinja2, 如下
# encoding=utf-8
import jinja2
import datetime
def strftime(dt, fmt):
print '0'
x = dt.strftime(fmt)
print '1'
return x
now = datetime.datetime.utcnow()
print jinja2.Template(u'''当前月份 {{ strftime(date, '%Y 年 %m 月') }}'''
).render(date=now, strftime=strftime)
x = dt.strftime(fmt)
这句. 好吧这 API 不知道适可而止一点么, 还是说喂过去的参数有什么问题? 那么看看从 Jinja2 传过来的东西到底是什么东西def strftime(dt, fmt):
print type(fmt)
return dt.strftime(fmt)
<type 'unicode'>
击倒. Jinja2 你这是要跟 strftime
合起来玩我啊. 好吧, 那只能这样了def strftime(dt, fmt):
print '0'
x = dt.strftime(fmt.encode('utf-8'))
print '1'
return x
行了, 那一个一个地攻略,
strftime
你先一边去def strftime(dt, fmt):
return '2013 年 4 月'
def strftime(dt, fmt):
return u'2013 年 4 月'
被抓到了尾巴了吧. Jinja2 不接受含有非 ASCII 的
str
而只能返回 unicode
. 好吧, 那这么一来, 线索又回到 strftime
了, 这函数一定是得出了什么不对劲的东西. 可以如下做个简单试验now = datetime.datetime.utcnow()
s = now.strftime('%Y 年 %m 月')
print type(s)
<type 'str'>
啊, 一瞬三观尽毁的感觉, 不带这么玩脱的吧. 行, 那么正确的打码方式是在返回的 str
上调用 decode
函数, 得到 unicode
再塞给 Jinja2. 完整代码如下# encoding=utf-8
import jinja2
import datetime
def strftime(dt, fmt):
return dt.strftime(fmt.encode('utf-8')).decode('utf-8')
now = datetime.datetime.utcnow()
print jinja2.Template(u'''当前月份 {{ strftime(date, '%Y 年 %m 月') }}'''
).render(date=now, strftime=strftime)
# encoding=utf-8
import jinja2
import datetime
def strftime(dt, fmt):
return dt.strftime(fmt.encode('utf-8')).decode('utf-8')
env = jinja2.Environment(loader=jinja2.DictLoader(
dict(test=u'''当前月份 {{ date|strftime('%Y 年 %m 月') }}''')))
env.filters['strftime'] = strftime
t = env.get_template('test')
print t.render(date=datetime.datetime.utcnow())