django 自定义标签和过滤器
django 自定义标签和过滤器
Django支持自定义标签和过滤器。起初还不太重视它这项功能,但最近试了试自定义标签。发现django这个功能实在是太爽了。
首先在你项目的一个app中建立一个python源文件夹,(即文件夹里面要包含一个__init__.py.)文件夹名为templatetags. 此文件夹便是存放自定义标签和过滤器的源码的地方了。
再如果是在templatetags文件夹中定义了标签,如 test_tags.py,要如何使用我们自定义的test_tags.py呢。很简单,只要在django的模板中加入{% load test_tags %},在test_tags.py 源文件中的自定义标签就可以在有load语句的模板中使用了。
下面详细的来谈谈建立自定义标签的过程以及方法。
1.建立项目,app的不说。只要在随意一个app中建立上文提到的templatetags文件夹。
这里是有点不理解的地方,在任意一个app建立的tags别的app能够使用吗?起初对此很疑惑。以为在一个app下建立的tags就这一个app能使用。为了大家都能够使用自己定义的tags,我还想把templatetags单独拿出来,跟普通app在项目当中是平级地位。这种思路搞了很久发现行不通。无奈只好打算使用copy在每一个app都复制一份templatetags。(当然这只是我起初的错误想法)但后来发现,居然一个app中有,其他的app中就可以直接使用此自定义的文件了。只需要在需要的模板当中(不管模板是在你的那个app中)调用load语句将自定义的文件load进来便可以。
随后看了看一些文档,只要templatetags所在位置是settings.py中INSTALLED_APPS中配置过的,或是在TEMPLATE_DIRS配置过的,任意一个位置便可以。
一定记得要在templatetags文件夹中包含__init__.py文件。空文件便可。
2.编写自定义文件代码test_tags.py。
现看看简单一点的过滤器(filter).
我的一个简单代码如下
- #!/usr/bin/env python
- #coding:utf-8
- from django import template
- register = template.Library()
- def percent_decimal(value):
- value = float(str(value))
- value = round(value, 3)
- value = value * 100
- return str(value) + '%'
- register.filter('percent_decimal', percent_decimal)
以上代码的意思是将传过来的小数转换成百分比显示。(django自带一个widthratio标签页可以完成此问题,但它的误差太大,小数位直接截掉了)。
其中register = template.Library(),register.filter('percent_decimal', percent_decimal) 两句是将所写代码注册到能用标签。最后一句的register.filter('percent_decimal', percent_decimal) 。第一个参数是字符串,就只在模板中使用时候的字符串比如{{12.09|percent_decimal}},名字可以自由取得。后面一个参数接受一个函数名,这个便是上面自定义的percent_decimal方法了。此方法中的value参数便是从模板中传递过来的参数。比如{{12.09|percent_decimal}}, percent_decimal方法接受到的value参数便是
12.09.
再来看看如何自定义tags。
自定义tags相对自定义filter要复杂一点。但仔细研究的话难度也不大。
自定义tags最基本的格式如下
- from django import template
- register = template.Library()
- class TestNode(template.Node):
- def __init__(self):
- pass
- def render(self, context):
- return "xxxxx"
- def test(parser, token):
- return TestNode()
- register.tag('my_tag', test)
其中包的引入和注册跟filter类型。主要的功能代码是一个类(继承自template.Node,所有的自定义tags都必须从这个类继承)和一个方法,以上代码什么功能都没有做,但我们要先搞明白它的实现机制。
如果从模板中使用以上的自定义tags,比如{% my_tag aaa.bbb %},则会调用上面代码注册的test方法,其中test方法中有两个参数,一个是parser,这个作用是挺大的,稍后说明。另一个是token,这个便是从模板当中使用的这个标签中所包含的所有字符串。如果使用的是{{ my_tag aaa.bbb }}则,token的值为“my_tag aaa.bbb”。注意是字符串类型。
随后调用TestNode类,当中有个render方法,其中有个参数是context,这个context参数就是在执行模板的渲染时由 View 传入的,在这里跟在被渲染的模板中可以调用同样的变量,可以试验在render中打印 "print user",便显示的是登陆用户的信息。
大概明白了自定义tags的实现机制后。看一个复杂一点点的代码。
- class PermissionLevel(template.Node):
- """
- 根据级别返回相应值
- """
- def __init__(self, sequence, text_level):
- self.sequence = sequence
- self.text_level = text_level
- pass
- def render(self, context):
- userInfo = context['user']
- level = 4 #拿到用户级别
- values = self.sequence.resolve(context, True)
- if self.text_level <= level:
- return str(values)
- else:
- return 'xxx'
- def do_permission_level(parser, token):
- try:
- tag_name, text_name, text_level = token.split_contents()
- except:
- raise template.TemplateSyntaxError, \
- "%r 标签语法错误,后面参数为两位,分别为变量名和该变量信息隐私等级" % \
- token.split_contents[0]
- try:
- text_level = int(text_level)
- except:
- raise TemplateSyntaxError, "permission_level标签语法错误,信息隐私等级应为整型数字"
- sequence = parser.compile_filter(text_name)
- return PermissionLevel(sequence, text_level)
- register.tag('permission_level', do_permission_level)
以上的代码完成的功能是判定一个信息的隐私级别,再根据登陆用户的权限来区分显示或不显示。在模板中的用法如下
{% permission_level objects.count 4 %}
其中permission_level 为标签名,objects.count是从view中传递过来的变量或属性。后面的 4 便是此属性(objects.count)的隐私级别为4,只有登录用户隐私级别大于它的时候才会显示,否则显示为“xxx”。
下面仔细来分析一下上面的代码。
tag_name, text_name, text_level = token.split_contents()
这一句将{% permission_level objects.count 4 %}分解成三个字符,其中text_name为“objects.count ”。这里跟自定义的filter不同,filter 是可以接受非字符的参数的,类型{{ 0.123|floatformat:2 }}也可以是{{ 0.123|floatformat:“2” }},后面的2参数加了引号。其中如果不加引号的话便传递的是非字符对象,像模板中的user是可以通过filter传递过去的。但tags不同,tag传递给注册的函数中的参数是字符串。
当时想到做自定义的tags也是项目需要。但自己开始做的时候发现tag传递的是字符串,当时也查了不少资料没找到解决的方法,那时就想放弃自定义tags了。但注意到django自带的tags不也是传递字符吗,像{%if user%}, {% for item in test_list %}这样的,django肯定有什么方法可以将类似“user”, "test_list"这样的字符和view中传递来的对象关联起来。于是便开始硬着头皮看django的源码了。(要知道对于像我这样的菜鸟来说。看源码可不是容易的事情)。
到底django是用什么魔法做到字符变对象的呢。起先我以为是这样的,比如{% if user %},则先提取user取来,再将传递过去的user从context中取去,user=context['user'],但后来一想,单个的对象好想是可以,但如果有句点符号的是怎么做到的呢。就像user.username?难道是再将它通过字符串操作再分离一次,但如果遇到多个句点符号呢?类似
user.profile.realname。照这个思路看源码,发现不是我想的这样的。有些源代码没看太懂,但正是这些没看懂的代码才是关键。
再回头看看上面的代码,注册标签的代码方法中有个parser参数,正是这个参数。
对照上面的代码。
sequence = parser.compile_filter(text_name)
将开始从token.split_contents()中分离出来的 像:user,,user.age , user.profile.realname等字符串先编译成一个sequence 对象。怎么编译的我也不太明白,不去管它了。
随后在类方法里面有句values = self.sequence.resolve(context, True)
再打印出values来看看,type(values)看看,哈哈。不再是字符串了,是真正的实例对象或是变量。
那么一切OK。虽然不知道Django中这两句是怎么样实现的,但不管了。至少现在我们可以自由的定制适合的filter和tags了。
django 自定义标签和过滤器的更多相关文章
- django中自定义标签和过滤器
想要实现自定义标签和过滤器需要进行准备工作: 准备(必需)工作: 1 在某个app下创建一个名为templatetags(必需,且包名不可变)的包.假设我们在名为polls的app下创建了一个tem ...
- python 全栈开发,Day70(模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介)
昨日内容回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...
- Django 模板 语法 变量 过滤器 模板继承 组件 自定义标签和过滤器 静态文件相关
本节目录 一 语法 二 变量 三 过滤器 四 标签Tags 五 模板继承 六 组件 七 自定义标签和过滤器 八 静态文件相关 一 语法 模板渲染的官方文档 关于模板渲染你只需要记两种特殊符号(语法 ...
- Django基础(2)--模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介
没整理完 昨日回顾: 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 ...
- Django模板自定义标签和过滤器,模板继承(extend),Django的模型层
上回精彩回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...
- Django框架(七)—— 模板层:变量、过滤器、标签、自定义标签和过滤器
目录 模板层:变量.过滤器.标签.自定义标签和过滤器 一.模板层变量 1.语法 2.使用 二.模板层之过滤器 1.语法 2.常用过滤器 3.其他过滤器 三.模板值标签 1.for标签 2.if标签 3 ...
- day58——模板继承、组件、自定义标签和过滤器、inclusion_tag、静态文件配置、url别名和反向解析、url命名空间
day58 模板相关 模板继承(母版继承) 1. 创建一个xx.html页面(作为母版,其他页面来继承它使用) 2. 在母版中定义block块(可以定义多个,整个页面任意位置) {% block co ...
- Django框架(六)--模板层:变量、过滤器、标签、自定义标签和过滤器
将页面的设计和Python的代码分离开会更干净简洁更容易维护. 我们可以使用 Django的 模板系统 (Template System)来实现这种模式 # django模板修改的视图函数 def c ...
- django自定义模板和过滤器
-自定义过滤器 -1 先app是不是已经在setting中注册 -2 在app下创建一个templatetags(****名字不能变***)的文件夹(模块) -3 在模块下创建一个py文件,名字随意: ...
随机推荐
- Java Servlet(一):创建工程(jdk7+tomcat7+eclipse)
本篇文件主要记录下怎么在jdk7+tomcat7下,使用eclipse创建并运行一个servlet工程. 安装具体步骤从网上搜索就可以找到,这里不再赘述. 在eclipse中切换到j2ee下, 从导航 ...
- 推荐几个sql server牛人的博客
Aaron Bertrand http://sqlblog.com/blogs/aaron_bertrand/ Brent Ozar www.brentozar.com/ Buck Woody htt ...
- Lintcode: Rehashing
The size of the hash table is not determinate at the very beginning. If the total size of keys is to ...
- 变形--原点 transform-origin
任何一个元素都有一个中心点,默认情况之下,其中心点是居于元素X轴和Y轴的50%处.如下图所示: 在没有重置transform-origin改变元素原点位置的情况下,CSS变形进行的旋转.位移.缩放,扭 ...
- demo06
city_data.xml <?xml version="1.0" encoding="utf-8"?> <resources> < ...
- 当执行php脚本时用户关闭浏览器会发生什么?
2008年8月16日 1,152 views 发表评论 阅读评论 如果一段php脚本执行插入数据到mysql的操作. 一般情况下,由于php脚本在服务器上执行,此时用户虽然关闭了浏览器,但是服务器端的 ...
- C# 多线程 lock 实例
class Program { static void Main(string[] args) { //在t1线程中调用LockMe,并将deadlock设为true(将出现死锁) int i = 1 ...
- libSVM的数据格式
首先介绍一下 libSVM的数据格式 Label 1:value 2:value -. Label:是类别的标识,比如上节train.model中提到的1 -1,你可以自己随意定,比如-10,0,15 ...
- android模拟器启动没有拨号功能
网上查询了很多资料, 其中一位网友给出的结论是android 4.3模拟器的bug, 如果在通讯录中添加好友,也是可以进行拨号的. 总结: 自认为是SDK安装程序不完整或设置AVD模拟器的时候设置项出 ...
- nginx for windows中的一项缺陷
按照官网上的说法,使用 start nginx 启动 nginx,使用 nginx -s quit 可以优雅地退出. 经实验,使用 start nginx 之后,会启动两个 nginx 的进程,据官网 ...