14 DTL 常用過濾器使用
Django 2.2 中提供了 60 多個內建的過濾器,已經能滿足我們的大部分場景。如果沒有適合我們業務場景的,還可以自定義過濾器。Django 的官方文件是做的非常齊全的,我們可以從官網上找到任何一個過濾器的說明和使用示例。在這裡會介紹部分常用的過濾器,同時可能結合原始碼探究過濾器內部實現原理。
1. 常用過濾器
1.1 add
將傳過來的引數加上某個值。示例:
{{ value|add:"2" }}
假設我們傳過來的值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 ''
如果value 和 add 的引數值都是數字, 那麼轉換的結果就是兩個數字之和;如果一個是字母字串,另一個引數值是數字字串,那麼返回的是空字串;如果兩個都是字母字串,那麼會執行字串相加。
1.2 capfirst
將 value 值得第一個字元大寫。如果第一字元非字母形式,那麼這個過濾器不會起作用。示例如下:
{{ value|capfirst }}
例如 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.3 center
將輸入的字元按照給定寬度居中。示例如下:
{{ value|center:"15" }}
假設輸入的 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))
這個過濾器實現也非常簡單,直接使用字串的 center() 方法,將字串的總寬度傳入即可自動將字串居中。
1.4 cut
移除 value 中所有指定字元。示例如下:
{{ value|cut:" " }}
假設輸入的字串為:‘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
可以看到這裡其實使用的就是 python 中字串的 replace() 方法進行移除指定字元的。
1.4 date
對輸入的時間字串按照指定格式輸出。示例如下:
{{ value|date:"D d M Y" }}
這裡引數含義較多,請參考官方文件。
1.5 default
如果 value 值可認為是 False (比如空字串,空字典、空陣列等),則使用給定的預設值,否則使用 value 的值。示例如下:
{{ value|default:"nothing" }}
和這個過濾器比較相近的一個叫做: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.6 dictsort
獲取字典列表,並按引數中給定的 key 值對列表進行排序。示例如下:
{{ value|dictsort:"name" }}
假設我們輸入的 value 值為:
[
{'name': 'zed', 'age': 19},
{'name': 'amy', 'age': 22},
{'name': 'joe', 'age': 31},
]
此時輸出的結果為:
[
{'name': 'amy', 'age': 22},
{'name': 'joe', 'age': 31},
{'name': 'zed', 'age': 19},
]
另外一種複雜的寫法如下:
{% for book in books|dictsort:"author.age" %}
* {{ book.title }} ({{ book.author.name }})
{% endfor %}
如果 value 的值為:
[
{'title': '1984', 'author': {'name': 'George', 'age': 45}},
{'title': 'Timequake', 'author': {'name': 'Kurt', 'age': 75}},
{'title': 'Alice', 'author': {'name': 'Lewis', 'age': 33}},
]
此時的輸出為:
* Alice (Lewis)
* 1984 (George)
* Timequake (Kurt)
上面的排序是按照從小到大的順序執行的,如果想要輸出按照從大到小的順序,可以使用 dictsortreversed 過濾器指令。
1.7 divisibleby
如果輸入 value 值可被引數整除,則輸出 True。用法如下:
{{ value|divisibleby:"3" }}
如果輸入值為 “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.8 escape
控制 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.9 filesizeformat
將輸入值轉換成易讀模式,特別是針對檔案大小,如 ‘13 KB’、‘2.4 MB’ 等。示例:
{{ value|filesizeformat }}
假設輸入的 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.10 first
該過濾器會返回輸入列表中的第一項。示例如下:
{{ value|first }}
如果是輸入的是列表 [‘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.11 join
使用指定字串連線輸出的列表或者字串,就像 python 中字串的 join() 方法。示例用法如下:
{{ value|join:" // " }}
假設輸入的值為['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.12 length
返回輸入值的長度,輸入的可以是字串和列表。示例如下:
{{ value|length }}
如果 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.14 floatformat
對資料進行四捨五入處理,引數是保留小數位數,可以為正負。若無引數 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 |
1.15 linenumbers
給輸如的文字加上行號。用法示例:
{{ value|linenumbers }}
假設輸入的文字為:
one
two
three
那麼輸出為:
1. one
2. two
3. three
檢視 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))
可以看到,其實 linenumbers 就是在每行文字的前面加上了一個從1開始的編號。
注意:程式碼中的 width 含義是確定最長行號所佔的字元寬度。比如編號1000,佔據4個字元長度,後面所有行程的行統一需要加上4個字元長度用於顯示行號。
1.16 ljust/rjust
ljust/rjust 分別表示以左對齊或者右對齊方式輸出 value 值。用法如下:
{{ value|ljsut:"10"}}
{{ vlaue|rjust:"10"}}
假設輸入的 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.17 lower/upper
lower/upper 過濾器表示對輸入的字串全部轉成大/小寫。用法如下:
{{ value|lower }}
{{ value|upper }}
假設 value 值為 “Hello, World!”,那麼第一個輸出為 “hello, world!”, 第二個輸出為 “HELLO, WORLD!”。
1.18 make_list
將輸入的 value 值轉成列表形式。如果輸入的是字串,則轉成字元列表。如果輸入的是整數,會首先轉成字串形式,然後在轉成列表。用法如下:
{{ value|make_list }}
假設輸入的 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.19 random
對於輸入的列表,從中任選一個元素返回。用法如下:
{{ value|random }}
1.20 slice
類似於 python 列表的 slice() 方法。示例如下:
{# 相當於some_list[:2] #}
{{ some_list|slice:":2" }}
假設輸入列表 [‘a’, ‘b’, ‘c’],那麼上述結果輸出 [‘a’, ‘b’]。
1.21 title
對輸入字串中每個單詞的首字母大寫。用法如下:
{{ value|title }}
假設 value 值為 “my FIRST post”, 輸出 “My First Post”
1.22 truncatechars
對輸入的字串進行擷取,引數 arg 含義是保留字串長度。如果字串長度超出了保留長度,那麼會保留 int(arg) -1
個字元並加上 ‘…’,一共是 int(arg)
個字元。示例如下:
{{ value|truncatechars:7 }}
假設 value 的值為 “Joel is a slug”,上述過濾結果為 “Joel i…”。
1.23 truncatewords
擷取固定單詞數的字串。引數 arg 的含義是保留單詞書。示例如下:
{{ value|truncatewords }}
假設 value 的值為 “Joel is a slug”,那麼上述過濾結果為 “Joel is …”。
1.24 urlencode
對 URL 中的特殊字元進行轉義。示例如下:
{{ value|urlencode }}
假設 value 值 為 https://www.example.org/foo?a=b&c=d"
, 上述過濾結果為: "https%3A//www.example.org/foo%3Fa%3Db%26c%3Dd"
。
1.25 wordcount
返回字串中單詞個數。示例如下:
{{ value|wordcount }}
假設 value 的值為 “Joel is a slug”,那麼上述過濾結果為4。
1.26 wordwrap
對於輸入的文字以指定長度換行。示例如下:
{{ value|wordwrap:5 }}
假設 value 的值為 “Joel is a slug”,那麼上述過濾結果為:
Joel
is a
slug
至此,比較常見的過濾器就算介紹完了,更多過濾器及其詳細用法可以參考官方文件以及相應的原始碼。
2. 小結
本小節我們介紹了 Django 中的常用過濾器,並給出了部分過濾器的實現程式碼。接下來,我們將學習在 Django 中如何實現自定義標籤和過濾器並使用。