Django自定义模板过滤器和标签的实现方法
作者:杜赛 发布时间:2023-08-12 12:24:51
现在我们已经很熟悉Django的MTV模式了。模板(template)负责如何去展示数据,而视图(view)负责筛选出正确的数据。因此通常来说逻辑都是放到视图中的,但模板也需要一些 和表示相关的逻辑 :比如循环展示(如 {% for ... %}
)、或者以某种特定格式输出(如 {{ ...|date:'Y-m-d' }}
)等,这些功能都是靠模板的 过滤器(filters) 和 标签(tags) 实现的。
Django的模板语言包含了很多内置的过滤器和标签,设计目的是满足应用需要占位逻辑需求。但有的时候这些通用的功能满足不了你的某些需求,这时候就需要自定义过滤器和标签来实现了。
前置条件
要在Django中使用模板过滤器或标签,就首先得 注册 它们。
注册方法如下:
在APP中新建名为 templatetags 的目录(方便起见,教程选择了 article 这个APP)
在此目录中新建名为 __init__.py 的空文件,使得此目录被视作一个Python的包
在此目录中新建python文件(比如 my_filters_and_tags.py ),就可以在里面愉快的写代码啦
完成后的目录结构如下:
article/
__init__.py
views.py
models.py
# 新增目录
templatetags/
__init__.py # 空文件
my_filters_and_tags.py # 即将写代码的地方
...
请注意:
目录必须位于已注册的APP中,这是出于安全性的考虑
新建目录后,必须手动重启服务器,里面的过滤器和标签才能生效
前置条件就完成了,接下来我们看看如何写一个模板过滤器。
模板过滤器
过滤器 filter 的表现形式为紧跟在上下文后面的管道符 | ,管道符后面是filter的名称: {{ ...|filter_name }} 。有的filter还可以带有参数: {{ ...|filter_name:var }}
。
注意过滤器名称的冒号后面不能有空格。
filter 这个名字可能会让你误认为它只是用来筛选某些特定数据的,但实际上它远不止这点功能。它可以改变上下文的最终展示效果,也可以将上下文通过运算输出为特定的值。
小试牛刀
要成为一个可用的 filter ,文件中必须包含一个名为 register
的模块级变量,它是一个 template.Library
实例,所有的 filters 均在其中注册。所以在 my_filter_and_tags.py
文件中输入以下内容:
article/templatetags/my_filter_and_tags.py
from django import template
register = template.Library()
接下来就可以像写普通的Python函数一样写过滤器了:
article/templatetags/my_filter_and_tags.py
from django import template
register = template.Library()
@register.filter(name='transfer')
def transfer(value, arg):
"""将输出强制转换为字符串 arg """
return arg
@register.filter()
def lower(value):
"""将字符串转换为小写字符"""
return value.lower()
filter可以通过装饰器进行注册。若注册装饰器中携带了 name 参数,则其值为此filter的名称;若未携带,则函数名就是filter的名称。
filter必须是有一到两个参数的Python函数。第一个参数是上下文本身,第二个参数则由filter提供。举个栗子,在过滤器 {{ var|foo:"bar" }}
中,变量 var 为第一个参数,变量 bar 则作为第二个参数。
调用这些filter的方法是在模板文件中用 {% load ... %}
将filter文件的名称加载进去,像这样:
# 任意模板文件中
{% load my_filters_and_tags %}
{{ 'ABC'|transfer:'cool' }} # 输出:'cool'
{{ 'ABC'|lower }} # 输出: 'abc'
更人性化的时间
了解完filter的使用方法后,下面来写点更实用的功能。
对人类这种生物来说, 相对时间 通常比 绝对时间 要更容易阅读。 发表于 3天前 可以轻易得知此文章刚发表不久;而 发表于 2019年8月10日 你还得想想今天到底几号来着。
因此写一个显示相对日期的 time_since_zh 过滤器:
article/templatetags/my_filter_and_tags.py
...
from django.utils import timezone
import math
# 获取相对时间
@register.filter(name='timesince_zh')
def time_since_zh(value):
now = timezone.now()
diff = now - value
if diff.days == 0 and diff.seconds >= 0 and diff.seconds < 60:
return '刚刚'
if diff.days == 0 and diff.seconds >= 60 and diff.seconds < 3600:
return str(math.floor(diff.seconds / 60)) + "分钟前"
if diff.days == 0 and diff.seconds >= 3600 and diff.seconds < 86400:
return str(math.floor(diff.seconds / 3600)) + "小时前"
if diff.days >= 1 and diff.days < 30:
return str(diff.days) + "天前"
if diff.days >= 30 and diff.days < 365:
return str(math.floor(diff.days / 30)) + "个月前"
if diff.days >= 365:
return str(math.floor(diff.days / 365)) + "年前"
代码功能很简单,就是将文章发布时间和当前时间作比较,然后返回适当的字符串。
修改文章列表模板文件中与发布时间相关的代码,把刚写的filter用上:
templates/article/list.html
...
{% load my_filters_and_tags %}
...
<!-- 旧代码
{{ article.created|date:'Y-m-d' }}
-->
<!-- 新代码 -->
{{ article.created|timesince_zh }}
...
效果如下:
实际上Django内置了一个 timesince 过滤器,只不过显示日期是英文的,不够友好。
模板标签
模板标签(tag)比过滤器更复杂,功能也更强大。
标签 tag 的表现形式为 {% tag_name ... %} ,比如我们非常熟悉的内置标签 {% url ... %} 、 {% static ... %} 等。如果内置标签满足不了你的需求,Django 提供了很多快捷方式,简化了编写绝大多数类型的标签过程。
简单标签
simple_tag 就是最重要的标签类型。标签的注册方法跟过滤器非常类似:
@register.simple_tag
def change_http_to_https(url):
new_url = url.replace('http://', 'https://')
return new_url
调用时同样记得在模板文件中用 {% load... %}
引入。用法你应该猜得到: {% change_http_to_https ... %}
,这个标签的作用是将http链接替换为https链接。
用 Django-allauth 进行微博登录,默认返回的用户头像是 http 链接(虽然微博有 https 版本的头像)。如果你的站点已经升级为 https 了,又不想花时间去研究微博的接口,那么这个标签就可以派上用场了。
顺带一说, Django-allauth 第三方登录的头像 url 保存在 User.socialaccount_set.all.0.get_avatar_url
中。
下面这个例子可以返回指定格式的时间字符串:
import datetime
@register.simple_tag
def current_time(format_string):
return datetime.datetime.now().strftime(format_string)
调用时你可以将其保存为模板变量,以便你在期望的位置多次调用:
{% current_time "%Y-%m-%d %I:%M %p" as the_time %}
<p>The time is {{ the_time }}.</p>
<p>Again, the time is {{ the_time }}.</p>
模板标签也可以访问当前的上下文,只需要在注册标签时传入 takes_context
参数:
@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 。
与过滤器不同的是,标签可以接受任意数量的位置或关键字参数。例如:
@register.simple_tag
def my_tag(a, b, *args, **kwargs):
warning = kwargs['warning']
profile = kwargs['profile']
...
return ...
在模板中调用时,任意数量的、以空格分隔的参数会被传递给模板标签。与 Python 中类似,关键字参数的赋值使用等号(" = "),且必须在位置参数后提供:
{% my_tag 123 "abcd" book.title warning=message profile=user.profile %}
包含标签
包含标签可以让另一个模板为当前模板渲染数据。听起来比较拗口,还是通过例子来理解。
假设现在有一个需求,是要在文章详情页面中,显示所有相关评论的发布时间。因此在 my_filter_and_tags.py
中写入:
my_filter_and_tags.py
...
@register.inclusion_tag('article/tag_list.html')
def show_comments_pub_time(article):
"""显示文章评论的发布时间"""
comments = article.comments.all()
return {'comments': comments}
函数传入的参数可以是模板中的上下文变量。函数体内部取得了所有相关评论的查询集,然后把结果 comments 返回。注意返回结果是进入到 tag_list.html
这个模板中去了,因此新建它并写入:
templates/article/tag_list.html
<ul>
{% for comment in comments %}
<li> {{ comment.created }} </li>
{% endfor %}
</ul>
然后在文章详情页面的模板中,随便找一个位置写入:
templates/article/detail.html
...
{% load my_filters_and_tags %}
...
{% show_comments_pub_time article %}
刷新详情页面,顺利的话就能看到所有评论的发表时间都展示出来了。
包含标签的另一个应用场景就是各种按钮了。有的按钮看上去长得都差不多,但是根据页面不同会有不同的功能,这时候也可以用包含标签来实现。
总之,包含标签可以将常用的模板代码打包成小组件,方便重复利用。
目前的博客项目中暂时还用不到包含标签,所以放心的删除上面的代码吧。
总结
以上所述是小编给大家介绍的Django自定义模板过滤器和标签的实现方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!
来源:https://segmentfault.com/a/1190000020109121


猜你喜欢
- 本文实例讲述了python中二维阵列的变换方法。分享给大家供大家参考。具体方法如下:先看如下代码:arr = [ [1, 2, 3], [4
- 求解列表中元素的排列和组合问题这个问题之前就遇到过几次没有太留意,最近在做题的时候遇上挺多的排列组合问题的,想来有必要温习一下了
- 我要实现的就是下图的这种样式,可参考下面这两个网站的留言板,他们的实现原理都是一样的畅言留言板样式:网易跟帖样式:原理需要在评论表添加两个主
- 在向表中插入数据的时候,经常遇到这样的情况:1、首先判断数据是否存在;2、如果不存在,则插入;3、如果存在,则更新。 在 SQL
- 当后端并未编写后台json数据,但是前端又要使用数据进行页面的编写,这是便可用mockjs编写一个假的数据源,发送ajax请求并获取数据1.
- 1.php安装。2.下载redis并编译(最好是在 /usr/local目录下运行该命令)# wget http://download.re
- 本文实例为大家分享了JavaScript实现九宫格抽奖的具体代码,供大家参考,具体内容如下看到个抽奖案例,觉得还不错。就自己做了一个简单版本
- Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的。相比于Angular.js,Vue.js提
- 今天来说说编程语言中的动态类型语言与鸭子类型。动态语言 * 对动态语言的定义:动态编程语言是一类在运行时可以改变其结构的语言:例如新的函数
- 本文实例讲述了MySQL基于DOS命令行登录操作方法。分享给大家供大家参考,具体如下:常用的MySQL命令行登录语句如下:mysql -h
- * 上有个有意思的话题叫细胞自动机:https://en.wikipedia.org/wiki/Cellular_automaton在2
- 引言 本文通过python3、第三方python库Selenium和谷歌浏览器Chrome,完成WPS表单的自动填写。开发环境配置 py
- 1.collatz序列编写一个名为 collatz()的函数,它 有一个名为 number 的参数。如果参数是偶数, 那么 collatz(
- 本文实例讲述了Python运算符重载用法。分享给大家供大家参考。具体分析如下:python中,我们在定义类的时候,可以通过实现一些函数来实现
- 在一些微服务或web服务中我们难免需要日志功能,用来记录一些用户的登录记录,操作记录,以及一些程序的崩溃定位,执行访问定位等等;Python
- 语法格式:row_number() over(partition by 分组列 order by 排序列 desc)row_num
- 制作网页可说是易学难精,因此,不断吸收经验可弥补不足,以下列出的50个制作主页的独门招数可帮助你尽快成为高手,哈哈!1、让读者有理由逗留。要
- (PS:本文假设你已经在本地联调好django和客户端,只是需要将django部署到外网)购买阿里云服务器到[阿里云官网],选择轻量应用服务
- MySQL Binary Log也就是常说的bin-log, ,是mysql执行改动产生的二进制日志文件,其主要作用有两个: * 数据回复
- 本文实例讲述了php验证session无效的解决方法。分享给大家供大家参考。具体方法如下:一、问题今天在配置 apache+php环境时折腾