自定义 Django 模板标签和过滤器

虽然 Django 已经给我们提供了足够丰富的内置标签和过滤器,但实际开发中还是会遇到内置标签和过滤器无法满足的需求,此时 Django 还给我们提供了实现自定义模板标签和过滤器的方式。想要自定义模板标签和过滤器,必须按照如下的方式做好前面的准备工作。

我们在前面的第一个应用 (hello_app) 中添加自定义的标签模板。首先完成如下两步:

  • 必须新建一个名为 templatetags 的目录。注意,这个名称固定,不能取其他名称。这个文件夹与该应用目录下的 models.pyviews.py 同级;
  • 该目录下要有一个 __init__.py 文件,此外还要新建一个用于定义标签和过滤器的文件: hello_extras.py

整个应用目录如下:

(django-manual) [root@server first_django_app]# tree hello_app/
hello_app/
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│   └── __init__.py
├── models.py
├── templatetags
│   ├── hello_extras.py
│   └── __init__.py
├── tests.py
├── urls.py
└── views.py

1. 自定义 Django 模板过滤器

首先,我们完成一个简单的 sum 过滤器,这个 sum 过滤器的作用是将输入的数组中的元素相加,用法示例如下:

{{ value|sum }}

假设输入的 value 值为 [1, 2, 3 ,4, 5],那么输出结果为15。Django 中并没有提供 sum 过滤器,我们需要自己首先这样一个 sum 过滤器。首先在hello_extras.py 文件中添加如下代码:

from django import template

register = template.Library()

@register.filter(name="sum")
def sum(value):
    if isinstance(value, list):
        try:
            sum = 0
            for i in range(len(value)):
                sum += int(value[i])
            return sum
        except Exception as e:
            return 0
    return value

首先是准备好过滤器函数 sum(),然后使用 register.filter() 注册该过滤器函数,这样我们自定义的 sum 过滤器就算完成了。接下来准备模板文件,在 template 目录下新建测试的模板文件,如下:

(django-manual) [root@server templates]# cat test_custom_filter.html 
{% load hello_extras %}
{{ value|sum }}

注意:第一步必须使用 load 标签导入我们自定义的过滤器模块,然后才能使用自定义的过滤器。

(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_custom_filter.html')
>>> content = tp.render(context={'value': [1, 2, 3, 4, 5]})
>>> print(content)

15

可以看到,我们已经成功实现了 sum 过滤器。

2. 自定义 Django 模板标签

自定义标签可以帮助我们做更多的事情,Django 帮助开发者在自定义模板标签上提供了一些快捷方式,使得开发者能够快速开发出各种自定义的标签,极大提升开发效率。 simple_tag() 方法就是其中的快捷方式之一。这个函数是django.template.Library 的一个方法。

假设我们想实现一个模板标签,显示当前的时间。我们在前面的 hello_extras.py 中添加如下模板标签代码:

import datetime
from django import template

register = template.Library()

@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

这里标签 current_time 可以带上一个参数,用于指定显示时间的格式。该标签的使用方式如下:

{% load hello_extras %}
{% current_time "%Y-%m-%d %H:%M:%S" as the_time %}
<p>The time is {{ the_time }}.</p>

我们继续使用 Django 的 shell 功能测试该模板标签:

(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_custom_filter.html')
>>> content = tp.render(context={})
>>> print(content)

<p>The time is 2020-03-30 13:46:07.</p>

可以看到,我们自定义的模板标签是生效了。除此之外,对于 simple_tag 自定义的标签还可以使用模板的上下文对象。例如:

@register.simple_tag(takes_context=True)
def current_time(context, format_string):
    timezone = context['timezone']
    # 调用自己的处理函数
    return your_get_current_time_method(timezone, format_string)

注意:这里自定义标签方法的第一个参数必须是 context。

最后 simple_tag 还可以接收位置参数和关键字参数,具体如下:

@register.simple_tag
def my_tag(a, b, *args, **kwargs):
    warning = kwargs['warning']
    profile = kwargs['profile']
    # 忽略...
    return ...

这样的自定义模板标签使用示例如下:

{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

3. 小结

这里我们通过两个简单的实战例子实现了自定义过滤器和自定义模板标签。至此,Django 的模板系统介绍可以正式告一段落了。接下来将学习 Django 的模型层。