Django 2.2 中提供了 60 多个内置的过滤器,已经能满足我们的大部分场景。如果没有适合我们业务场景的,还可以自定义过滤器。Django 的官方文档是做的非常齐全的,我们可以从官网上找到任何一个过滤器的说明和使用示例。在这里会介绍部分常用的过滤器,同时可能结合源码探究过滤器内部实现原理。
将传过来的参数加上某个值。示例:
{{ value|add:"2" }}
代码块预览 复制
- 1
假设我们传过来的值4,那么翻译的结果将会变成6。那么如果 add 的参数是一个字符串呢,输出结果是怎么样的?我们来看下这个过滤器实现的代码就能知道结果了:
@register.filter(is_safe=False) def add(value, arg): """Add the arg to the value.""" try: return int(value) + int(arg) except (ValueError, TypeError): try: return value + arg except Exception: return ''
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如果value 和 add 的参数值都是数字, 那么转换的结果就是两个数字之和;如果一个是字母字符串,另一个参数值是数字字符串,那么返回的是空字符串;如果两个都是字母字符串,那么会执行字符串相加。
将 value 值得第一个字符大写。如果第一字符非字母形式,那么这个过滤器不会起作用。示例如下:
{{ value|capfirst }}
代码块预览 复制
- 1
例如 value 值为 “imooc”,那么过滤器输出的结果为 “Imooc”。该过滤器的实现代码如下:
@register.filter(is_safe=True) @stringfilter def capfirst(value): """Capitalize the first character of the value.""" return value and value[0].upper() + value[1:]
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
将输入的字符按照给定宽度居中。示例如下:
{{ value|center:"15" }}
代码块预览 复制
- 1
假设输入的 value 值为 “imooc”,那么输出的字符串为" imooc "。来继续查看该过滤器实现代码:
@register.filter(is_safe=True) @stringfilter def center(value, arg): """Center the value in a field of a given width.""" return value.center(int(arg))
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
这个过滤器实现也非常简单,直接使用字符串的 center() 方法,将字符串的总宽度传入即可自动将字符串居中。
移除 value 中所有指定字符。示例如下:
{{ value|cut:" " }}
代码块预览 复制
- 1
假设输入的字符串为:‘hello world, game over’,那么经过此过滤器后,得到的结果为:‘helloworld,gameover’
@register.filter @stringfilter def cut(value, arg): """Remove all values of arg from the given string.""" safe = isinstance(value, SafeData) value = value.replace(arg, '') if safe and arg != ';': return mark_safe(value) return value
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可以看到这里其实使用的就是 python 中字符串的 replace() 方法进行移除指定字符的。
对输入的时间字符串按照指定格式输出。示例如下:
{{ value|date:"D d M Y" }}
代码块预览 复制
- 1
这里参数含义较多,请参考官方文档。
如果 value 值可认为是 False (比如空字符串,空字典、空数组等),则使用给定的默认值,否则使用 value 的值。示例如下:
{{ value|default:"nothing" }}
代码块预览 复制
- 1
和这个过滤器比较相近的一个叫做:default_if_none,它是只在 value 值为 None 时,才会输出默认值,否则依旧使用 value 的值:
(django-manual) [root@server first_django_app]# cat templates/test_filter.html {{ empty|default:"nothing" }} {{ empty_if_none|default_if_none:"none" }} (django-manual) [root@server first_django_app]# python manage.py shell Python 3.8.1 (default, Dec 24 2019, 17:04:00) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.template.loader import get_template >>> tp = get_template('test_filter.html') >>> print(tp.render(context={'empty':{}, 'empty_if_none':{}})) nothing {} >>> print(tp.render(context={'empty':[], 'empty_if_none':[]})) nothing [] >>> print(tp.render(context={'empty':'', 'empty_if_none':''})) nothing >>> print(tp.render(context={'empty':None, 'empty_if_none':None})) nothing none
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
获取字典列表,并按参数中给定的 key 值对列表进行排序。示例如下:
{{ value|dictsort:"name" }}
代码块预览 复制
- 1
假设我们输入的 value 值为:
[ {'name': 'zed', 'age': 19}, {'name': 'amy', 'age': 22}, {'name': 'joe', 'age': 31}, ]
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
此时输出的结果为:
[ {'name': 'amy', 'age': 22}, {'name': 'joe', 'age': 31}, {'name': 'zed', 'age': 19}, ]
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
另外一种复杂的写法如下:
{% for book in books|dictsort:"author.age" %} * {{ book.title }} ({{ book.author.name }}) {% endfor %}
代码块预览 复制
- 1
- 2
- 3
如果 value 的值为:
[ {'title': '1984', 'author': {'name': 'George', 'age': 45}}, {'title': 'Timequake', 'author': {'name': 'Kurt', 'age': 75}}, {'title': 'Alice', 'author': {'name': 'Lewis', 'age': 33}}, ]
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
此时的输出为:
* Alice (Lewis) * 1984 (George) * Timequake (Kurt)
代码块预览 复制
- 1
- 2
- 3
上面的排序是按照从小到大的顺序执行的,如果想要输出按照从大到小的顺序,可以使用 dictsortreversed 过滤器指令。
如果输入 value 值可被参数整除,则输出 True。用法如下:
{{ value|divisibleby:"3" }}
代码块预览 复制
- 1
如果输入值为 “21”,则输出为 True。该指令实现的代码如下,非常简单。
@register.filter(is_safe=False) def divisibleby(value, arg): """Return True if the value is divisible by the argument.""" return int(value) % int(arg) == 0
代码块预览 复制
- 1
- 2
- 3
- 4
控制 HTML 转义,替换 value 中的某些 HTML 特殊字符。
<
is converted to<
;>
is converted to>
;'
(single quote) is converted to'
;"
(double quote) is converted to&quto
;&
is converted to&
{% autoescape off %} {{ title|escape }} {% endautoescape %}
代码块预览 复制
- 1
- 2
- 3
将输入值转换成易读模式,特别是针对文件大小,如 ‘13 KB’、‘2.4 MB’ 等。示例:
{{ value|filesizeformat }}
代码块预览 复制
- 1
假设输入的 value 值为 123456789,输出结果为 “117.7 MB”。其实现代码如下:
@register.filter(is_safe=True) def filesizeformat(bytes_): """ Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102 bytes, etc.). """ try: bytes_ = float(bytes_) except (TypeError, ValueError, UnicodeDecodeError): value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0} return avoid_wrapping(value) def filesize_number_format(value): return formats.number_format(round(value, 1), 1) KB = 1 << 10 MB = 1 << 20 GB = 1 << 30 TB = 1 << 40 PB = 1 << 50 # 如果是负数,需要转成正数然后转换 negative = bytes_ < 0 if negative: bytes_ = -bytes_ # Allow formatting of negative numbers. # if bytes_ < KB: value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {'size': bytes_} elif bytes_ < MB: value = gettext("%s KB") % filesize_number_format(bytes_ / KB) elif bytes_ < GB: value = gettext("%s MB") % filesize_number_format(bytes_ / MB) elif bytes_ < TB: value = gettext("%s GB") % filesize_number_format(bytes_ / GB) elif bytes_ < PB: value = gettext("%s TB") % filesize_number_format(bytes_ / TB) else: value = gettext("%s PB") % filesize_number_format(bytes_ / PB) # 如果是负数,转换后再加上-号 if negative: value = "-%s" % value # 去掉'\0xa0'字符 return avoid_wrapping(value)
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
该过滤器会返回输入列表中的第一项。示例如下:
{{ value|first }}
代码块预览 复制
- 1
如果是输入的是列表 [‘a’, ‘b’, ‘c’],那么输出的为 ‘a’。和 first 指令相反作用的过滤器为 last,对于本次输出的结果为 ‘c’。first 和 last 过滤器的实现代码如下,非常简单:
@register.filter(is_safe=False) def first(value): """Return the first item in a list.""" try: return value[0] except IndexError: return '' @register.filter(is_safe=True) def last(value): """Return the last item in a list.""" try: return value[-1] except IndexError: return ''
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
使用指定字符串连接输出的列表或者字符串,就像 python 中字符串的 join() 方法。示例用法如下:
{{ value|join:" // " }}
代码块预览 复制
- 1
假设输入的值为['a', 'b', 'c']
,那么输出为 “a // b // c”。它的实现代码如下:
@register.filter(is_safe=True, needs_autoescape=True) def join(value, arg, autoescape=True): """Join a list with a string, like Python's ``str.join(list)``.""" try: if autoescape: value = [conditional_escape(v) for v in value] # 最核心的处理是使用字符串的join()方法 data = conditional_escape(arg).join(value) except TypeError: # Fail silently if arg isn't iterable. return value return mark_safe(data)
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
返回输入值的长度,输入的可以是字符串和列表。示例如下:
{{ value|length }}
代码块预览 复制
- 1
如果 value 的值为:[‘a’, ‘b’, ‘c’, ‘d’] 或者 ‘abcd’,输出都是4。对于未定义的变量值,输出为0。它的实现代码也是非常简单,如下:
@register.filter(is_safe=False) def length(value): """Return the length of the value - useful for lists.""" try: return len(value) except (ValueError, TypeError): return 0
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
对数据进行四舍五入处理,参数是保留小数位数,可以为正负。若无参数 arg, 默认保留1位小数。
用法示例1:不带参数
Value | Template | Output |
---|---|---|
34.23234 | {{ value|floatformat }} | 34.2 |
34.00000 | {{ value|floatformat }} | 34 |
34.26000 | {{ value|floatformat }} | 34.3 |
示例用法2:带上正参数,保留有效参数位
Value | Template | Output |
---|---|---|
34.23234 | {{ value|floatformat:3 }} | 34.232 |
34.00000 | {{ value|floatformat:3 }} | 34.000 |
34.26000 | {{ value|floatformat:3 }} | 34.260 |
示例用法3:带上0参数,即四舍五入取整输出
Value | Template | Output |
---|---|---|
34.23234 | {{ value|floatformat:“0” }} | 34 |
34.00000 | {{ value|floatformat:“0” }} | 34 |
39.56000 | {{ value|floatformat:“0” }} | 40 |
示例用法3:带上负参数,对于没有小数显示的则会默认取整。
Value | Template | Output |
---|---|---|
34.23234 | {{ value|floatformat:"-3" }} | 34.232 |
34.00000 | {{ value|floatformat:"-3" }} | 34 |
34.26000 | {{ value|floatformat:"-3" }} | 24.269 |
给输如的文本加上行号。用法示例:
{{ value|linenumbers }}
代码块预览 复制
- 1
假设输入的文本为:
one two three
代码块预览 复制
- 1
- 2
- 3
那么输出为:
1. one 2. two 3. three
代码块预览 复制
- 1
- 2
- 3
查看 Django 内部源码,可以看到 linenumbers 过滤器实现的代码:
@register.filter(is_safe=True, needs_autoescape=True) @stringfilter def linenumbers(value, autoescape=True): """Display text with line numbers.""" lines = value.split('\n') # Find the maximum width of the line count, for use with zero padding # string format command width = str(len(str(len(lines)))) if not autoescape or isinstance(value, SafeData): for i, line in enumerate(lines): lines[i] = ("%0" + width + "d. %s") % (i + 1, line) else: for i, line in enumerate(lines): lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) return mark_safe('\n'.join(lines))
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
可以看到,其实 linenumbers 就是在每行文本的前面加上了一个从1开始的编号。
注意:代码中的 width 含义是确定最长行号所占的字符宽度。比如编号1000,占据4个字符长度,后面所有行程的行统一需要加上4个字符长度用于显示行号。
ljust/rjust 分别表示以左对齐或者右对齐方式输出 value 值。用法如下:
{{ value|ljsut:"10"}} {{ vlaue|rjust:"10"}}
代码块预览 复制
- 1
- 2
假设输入的 value 值为 “imooc”,第一个输出为 "imooc “,第二个输出为 " imooc”。其过滤器实现代码分别如下:
@register.filter(is_safe=True) @stringfilter def ljust(value, arg): """Left-align the value in a field of a given width.""" return value.ljust(int(arg)) @register.filter(is_safe=True) @stringfilter def rjust(value, arg): """Right-align the value in a field of a given width.""" return value.rjust(int(arg))
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
lower/upper 过滤器表示对输入的字符串全部转成大/小写。用法如下:
{{ value|lower }} {{ value|upper }}
代码块预览 复制
- 1
- 2
假设 value 值为 “Hello, World!”,那么第一个输出为 “hello, world!”, 第二个输出为 “HELLO, WORLD!”。
将输入的 value 值转成列表形式。如果输入的是字符串,则转成字符列表。如果输入的是整数,会首先转成字符串形式,然后在转成列表。用法如下:
{{ value|make_list }}
代码块预览 复制
- 1
假设输入的 value 值为 “imooc”,输出结果为 [‘i’, ‘m’, ‘o’, ‘o’, ‘c’]。如果输入的是 123,那么输出为 [‘1’, ‘2’, ‘3’]。它的实现过程非常简单,对输入的文本直接使用 list() 方法并返回。
@register.filter(is_safe=False) @stringfilter def make_list(value): """ Return the value turned into a list. For an integer, it's a list of digits. For a string, it's a list of characters. """ return list(value)
代码块预览 复制
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
对于输入的列表,从中任选一个元素返回。用法如下:
{{ value|random }}
代码块预览 复制
- 1
类似于 python 列表的 slice() 方法。示例如下:
{# 相当于some_list[:2] #} {{ some_list|slice:":2" }}
代码块预览 复制
- 1
- 2
假设输入列表 [‘a’, ‘b’, ‘c’],那么上述结果输出 [‘a’, ‘b’]。
对输入字符串中每个单词的首字母大写。用法如下:
{{ value|title }}
代码块预览 复制
- 1
假设 value 值为 “my FIRST post”, 输出 “My First Post”
对输入的字符串进行截取,参数 arg 含义是保留字符串长度。如果字符串长度超出了保留长度,那么会保留 int(arg) -1
个字符并加上 ‘…’,一共是 int(arg)
个字符。示例如下:
{{ value|truncatechars:7 }}
代码块预览 复制
- 1
假设 value 的值为 “Joel is a slug”,上述过滤结果为 “Joel i…”。
截取固定单词数的字符串。参数 arg 的含义是保留单词书。示例如下:
{{ value|truncatewords }}
代码块预览 复制
- 1
假设 value 的值为 “Joel is a slug”,那么上述过滤结果为 “Joel is …”。
对 URL 中的特殊字符进行转义。示例如下:
{{ value|urlencode }}
代码块预览 复制
- 1
假设 value 值 为 https://www.example.org/foo?a=b&c=d"
, 上述过滤结果为: "https%3A//www.example.org/foo%3Fa%3Db%26c%3Dd"
。
返回字符串中单词个数。示例如下:
{{ value|wordcount }}
代码块预览 复制
- 1
假设 value 的值为 “Joel is a slug”,那么上述过滤结果为4。
对于输入的文本以指定长度换行。示例如下:
{{ value|wordwrap:5 }}
代码块预览 复制
- 1
假设 value 的值为 “Joel is a slug”,那么上述过滤结果为:
Joel is a slug
代码块预览 复制
- 1
- 2
- 3
至此,比较常见的过滤器就算介绍完了,更多过滤器及其详细用法可以参考官方文档以及相应的源代码。
本小节我们介绍了 Django 中的常用过滤器,并给出了部分过滤器的实现代码。接下来,我们将学习在 Django 中如何实现自定义标签和过滤器并使用。