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

我的一个简单代码如下

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

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

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

  1. django中自定义标签和过滤器

    想要实现自定义标签和过滤器需要进行准备工作: 准备(必需)工作: 1  在某个app下创建一个名为templatetags(必需,且包名不可变)的包.假设我们在名为polls的app下创建了一个tem ...

  2. python 全栈开发,Day70(模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介)

    昨日内容回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...

  3. Django 模板 语法 变量 过滤器 模板继承 组件 自定义标签和过滤器 静态文件相关

    本节目录 一 语法 二 变量 三 过滤器 四 标签Tags 五 模板继承 六 组件 七 自定义标签和过滤器 八 静态文件相关 一 语法   模板渲染的官方文档 关于模板渲染你只需要记两种特殊符号(语法 ...

  4. Django基础(2)--模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介

    没整理完 昨日回顾: 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 ...

  5. Django模板自定义标签和过滤器,模板继承(extend),Django的模型层

    上回精彩回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...

  6. Django框架(七)—— 模板层:变量、过滤器、标签、自定义标签和过滤器

    目录 模板层:变量.过滤器.标签.自定义标签和过滤器 一.模板层变量 1.语法 2.使用 二.模板层之过滤器 1.语法 2.常用过滤器 3.其他过滤器 三.模板值标签 1.for标签 2.if标签 3 ...

  7. day58——模板继承、组件、自定义标签和过滤器、inclusion_tag、静态文件配置、url别名和反向解析、url命名空间

    day58 模板相关 模板继承(母版继承) 1. 创建一个xx.html页面(作为母版,其他页面来继承它使用) 2. 在母版中定义block块(可以定义多个,整个页面任意位置) {% block co ...

  8. Django框架(六)--模板层:变量、过滤器、标签、自定义标签和过滤器

    将页面的设计和Python的代码分离开会更干净简洁更容易维护. 我们可以使用 Django的 模板系统 (Template System)来实现这种模式 # django模板修改的视图函数 def c ...

  9. django自定义模板和过滤器

    -自定义过滤器 -1 先app是不是已经在setting中注册 -2 在app下创建一个templatetags(****名字不能变***)的文件夹(模块) -3 在模块下创建一个py文件,名字随意: ...

随机推荐

  1. 使用NBU进行oracle异机恢复

    windows平台的异机恢复,目录不同 1.异机环境准备安装oracle介质安装nbu客户端在异机主机的host文件中添加nbu server主机和原主机信息 2.恢复spfile文件 C:\> ...

  2. 用get方式提交请求的url带有中文参数

    又碰到JSP页面中文乱码问题,经过一次encodeURI处理后仍旧是乱码,后来经过两次encodeURI后正常显示中文 以前也碰到过同样的问题,没深究,这次网上搜集了一些资料,记录下来留做备份 ___ ...

  3. Java线程同步和线程通信

    一.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步. 不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全. 1.同步 ...

  4. eclipse Project facet Java version 1.8 is not supported.

    在移植eclipse项目时,如果遇到 “Project facet Java version 1.7 is not supported.” 项目中的jdk1.7不支持.说明项目是其他版本jdk编译的, ...

  5. Leetcode: Self Crossing

    You are given an array x of n positive numbers. You start at point (0,0) and moves x[0] metres to th ...

  6. c# 隐藏 控制台应用程序

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...

  7. [原创]java WEB学习笔记73:Struts2 学习之路-- strut2中防止表单重复提交

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  8. JavaScript: basis

    ref: http://www.imooc.com/code/387 1. html里直接嵌入js: <!DOCTYPE HTML> <html> <head> & ...

  9. Android设计模式---观察者模式小demo(一)

    1,今天刚好看到了设计模式这一块来,而观察者模式是我一直想总结的,先来看看观察者模式的简单的定义吧 "当一个对象改变时,他的所有依赖者都会受到通知,并自动更新." 一般我们项目中就 ...

  10. paper 60 :转载关于视觉SCI期刊

    ChanLee_1整理的计算机视觉领域稍微容易中的期刊 模式识别,计算机视觉领域,期刊 (1)pattern recognition letters, 从投稿到发表,一年半时间 (2)Pattern  ...