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).

我的一个简单代码如下

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2. #coding:utf-8  
  3.   
  4. from django import template  
  5.   
  6. register = template.Library()  
  7.   
  8. def percent_decimal(value):  
  9.       
  10.     value = float(str(value))  
  11.     value = round(value, 3)  
  12.     value = value * 100  
  13.       
  14.     return str(value) + '%'  
  15. 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最基本的格式如下

Python代码   收藏代码
  1. from django import template  
  2.   
  3. register = template.Library()  
  4.   
  5. class TestNode(template.Node):  
  6.     def __init__(self):  
  7.         pass  
  8.   
  9.     def render(self, context):  
  10.         return "xxxxx"  
  11.   
  12. def test(parser, token):  
  13.     return TestNode()  
  14.   
  15. 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的实现机制后。看一个复杂一点点的代码。

Python代码   收藏代码
  1. class PermissionLevel(template.Node):  
  2.       
  3.     """ 
  4.     根据级别返回相应值 
  5.     """      
  6.       
  7.     def __init__(self, sequence, text_level):  
  8.         self.sequence = sequence  
  9.         self.text_level = text_level  
  10.         pass  
  11.     def render(self, context):  
  12.         userInfo = context['user']  
  13.         level = 4  #拿到用户级别  
  14.           
  15.         values = self.sequence.resolve(context, True)  
  16.         if self.text_level <= level:  
  17.             return str(values)  
  18.         else:  
  19.              return 'xxx'  
  20.   
  21. def do_permission_level(parser, token):  
  22.           
  23.     try:  
  24.         tag_name, text_name, text_level = token.split_contents()  
  25.     except:  
  26.         raise template.TemplateSyntaxError, \  
  27.                    "%r 标签语法错误,后面参数为两位,分别为变量名和该变量信息隐私等级" % \  
  28.                     token.split_contents[0]  
  29.     try:  
  30.         text_level = int(text_level)  
  31.     except:  
  32.         raise TemplateSyntaxError, "permission_level标签语法错误,信息隐私等级应为整型数字"  
  33.     sequence = parser.compile_filter(text_name)  
  34.       
  35.     return PermissionLevel(sequence, text_level)  
  36.        
  37. 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了。


原博客地址:http://xiao80xiao.iteye.com/blog/519394

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值