昨天写flask模板,在使用过滤器truncate的时候发现并没有截断字符串,多次尝试都没用,还以为是jinja2模板的一个bug,百度了一下也没有找到解决的方法,于是就只好看一下源码,终于被我解决了,发出来分享,如果文中有错误的地方,还望各位大佬多多指教!
先上结果:其实不是没用,只是没有达到长度!!!!!
解决办法很简单,增加一个参数:leeway=0
truncate(6, killwords=True, leeway=0)
现在你马上就能看到效果了。
如果只是解决问题的话,到这就可以了。下面来讲一下原理
首先看一下官方源码:
@environmentfilter
def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None):
"""Return a truncated copy of the string. The length is specified
with the first parameter which defaults to ``255``. If the second
parameter is ``true`` the filter will cut the text at length. Otherwise
it will discard the last word. If the text was in fact
truncated it will append an ellipsis sign (``"..."``). If you want a
different ellipsis sign than ``"..."`` you can specify it using the
third parameter. Strings that only exceed the length by the tolerance
margin given in the fourth parameter will not be truncated.
.. sourcecode:: jinja
{{ "foo bar baz qux"|truncate(9) }}
-> "foo..."
{{ "foo bar baz qux"|truncate(9, True) }}
-> "foo ba..."
{{ "foo bar baz qux"|truncate(11) }}
-> "foo bar baz qux"
{{ "foo bar baz qux"|truncate(11, False, '...', 0) }}
-> "foo bar..."
The default leeway on newer Jinja2 versions is 5 and was 0 before but
can be reconfigured globally.
"""
if leeway is None:
leeway = env.policies['truncate.leeway']
assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length)
assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway
if len(s) <= length + leeway:
return s
if killwords:
return s[:length - len(end)] + end
result = s[:length - len(end)].rsplit(' ', 1)[0]
return result + end
注释写的挺好的,就不翻译了,有需要的可以直接复制,然后到翻译软件上面去翻译
重点来看一下参数:
- env 系统默认的环境变量,不管它
- s 传入的字符串
- length 截断后字符串想要保留的长度
- killwords 是否截断单词(当然了,对我们博大精深的中文是没用的,毕竟也不能把中文拆成两半啊)
- end 结束的符号,默认是
- leeway 容差(自己翻译的【手动滑稽】翻译软件的结果是“留有余地”),这个是重点!!!
下面来看一下功能(直接逐行翻译吧):
def do_truncate(env, s, length=255, killwords=False, end='...', leeway=None):
# 判断是否传入leeway,如果没有,默认是为None
if leeway is None:
# 使用系统环境里面的leeway配置
leeway = env.policies['truncate.leeway']
# 断言:限制的长度必须大于末尾的字符串的长度,否则提示后面的信息
assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length)
# 断言:容差必须是大于0的正数,如果为负数,则提示后面的信息
assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway
# 上面是准备工作,配置好环境,符合条件才能开始正常工作嘛
# 下面是对传入的字符串的操作
# 如果字符串长度小于限制的长度 + 容差值,直接返回传入的字符串
if len(s) <= length + leeway:
return s
# 到达这里就是长度大于限制长度 + 容差值了,那就需要对字符串进行处理了
# 如果killwords=True
if killwords:
# 返回的字符串为:长度为(指定长度 - 结束符长度)的字符串 + 结束符。
return s[:length - len(end)] + end
# 到这里就是killwords=False,就是截断字符串了
# 先截取(指定长度 - 结束符长度)的字符串,用rsplit(从右往左切割)切割,以空格为分隔符,切割一次,取前面部分
# 示例:"How are you?" 截取10个长度为"How are yo"
# 按空格从右往左切割一次后得到列表["How are", "yo"]
# 最后取列表的第一个值,就是"How are"
result = s[:length - len(end)].rsplit(' ', 1)[0]
# 最后将字符串"How are"和结束符"..."拼接在一起后返回
return result + end
这就是原理了,我们来检验一下是否正确吧!
结果和我们预料的一样
注意,我这里指定了参数leeway=0,如果不指定的话,结果肯定会不一样的,在源码中作者注释里面有说明;
leeway参数默认值在新版本中是:5,在以前的版本中是:0
我用的版本是django2.1,新版本,所以默认值是5,如果不指定的话,那么在指定长度的基础上,超出5个长度都是不会被截断的,这点一定要注意,这也是为什么有时候我们明明设置了指定长度但是却没有截断效果的原因。
来看看不指定leeway=0的效果:
我们指定的字符串长度为10,可是这都已经12了,但是还是没有截断,因为这个时候,实际上允许的长度已经是15(我们指定的10 + 系统默认的leeway=5)
好了!!!终于解决这个问题了!不是django模板有缺陷,而是我们的操作不对。有时候还是要看看源码才行啊!