侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Django自定义模板过滤器和标签的实现方法

框架(架构)  /  管理员 发布于 7年前   161

现在我们已经很熟悉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.pyfrom django import templateregister = template.Library()

接下来就可以像写普通的Python函数一样写过滤器了:

article/templatetags/my_filter_and_tags.pyfrom django import templateregister = template.Library()@register.filter(name='transfer')def transfer(value, arg):  """将输出强制转换为字符串 arg """  return [email protected]()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 timezoneimport 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_tagdef 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 [email protected]_tagdef 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_tagdef 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 中写入:

[email protected]_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自定义模板过滤器和标签的实现方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!


  • 上一条:
    django写用户登录判定并跳转制定页面的实例
    下一条:
    扩展Django admin的list_filter()可使用范围方法
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Filament v3.1版本发布(0个评论)
    • docker + gitea搭建一个git服务器流程步骤(0个评论)
    • websocket的三种架构方式使用优缺点浅析(0个评论)
    • ubuntu20.4系统中宿主机安装nginx服务,docker容器中安装php8.2实现运行laravel10框架网站(0个评论)
    • phpstudy_pro(小皮面板)中安装最新php8.2.9版本流程步骤(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2018-05
    • 2020-02
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-11
    • 2021-03
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-08
    • 2023-08
    • 2023-10
    • 2023-12
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客