Django Template的一些注意点

Django Template的if标签

在Django的早期版本中,if标签的功能不是太强,只能检查一个变量是否为真(即,变量存在,非空,不是布尔值假),不支持==、!=、>、>=、<、<=等比较。如果要比较两个值是否相等,就要使用ifequal标签。
在Django模板系统中,以下这些对象相当于布尔值的False

1
2
3
4
5
6
7
空列表([])
空元组(())
空字典({}
空字符串(''
零值(0)
特殊对象None
对象False

除这些之外都被视为True。if标签还接受and、or和not来对多个变量做判断,但是and和or不能混用,因为可能会造成逻辑混乱,因为if标签不支持通过()来改变运算优先级。
不过在较高版本的Django中,这些都得到了不同程度的改善。例如:1. 支持==、>这些运算符来对两个对象进行比较;2. and和or可以混用,但是注意一点,and运算的优先级更高。不过由于if标签还是不支持(),所以还是尽量把运算放在后端而不要混用and和or。

Django Template中的变量

在Django Template中,我们通过上下文传入的变量,但是变量调用方法时会受到一定的限制,变量只能调用无参的方法,如一个str对象,它只能调用如upper之类的无参对象,调用方式如下:

1
2
3
4
{{ string.upper }} # correct
{{ string.upper() }} #error
{{ string.index('a') }} #error
{{ string[0:-1]}} #error

对于一个可索引对象,如list、set、dict等,我们在Template中可以通过以下方式获得其中的指定对象:

1
2
3
{{ list.0 }} # list[0]
{{ set.0 }} # set[0]
{{ dict.a }} # dict['a']

Django Template自定义过滤器

Django利用过滤器大大强化了Template的功能,在Template利用管道符|来调用,调用的一般形式如下:

1
2
{{ value | filter }} # 没有额外的参数
{{ value | filter : arg }} #带有一个额外的参数

其中,value是变量,arg为选项值
不过在很多情况下,我们需要自定义一些过滤器,我们在我们的一个app目录建立templatetags/在此目录下建立空文件init.py和myfilter.py,首先插入几行代码:

1
2
from django import template
register = template.Library()

其中register是用来注册过滤器的,过滤器函数必须要返回一些信息,即使出错,也要返回空字符串或者默认值,过滤器分为有参和无参两种:

1
2
3
4
5
6
7
def remove(value, arg):
# 移除字符串中value的arg字串
return value.replace(arg, '')

def lower(value):
# 将字符串value转为小写字母
return value.lower()

接下去的我们我们要注册自定义的过滤器:

1
2
3
4
# 第一个参数是在模板中使用的过滤器的名字
# 第二个就是你的过滤器函数引用名
register.filter('remove', remove)
register.filter('lower', lower)

在Python2.4以上的版本中,可以使用装饰器来注册:

1
2
3
4
5
6
@register.filter(name='remove')
def remove(value, arg):
return value.replace(arg, '')
@register.filter # 使用函数名作为过滤器名
def lower(value):
return value.lower()

如果要指定传入的value的类型,也可以使用装饰器:

1
2
from django.template.defaultfilters import stringfilter
@stringfilter # 希望字符串作为参数

最终,完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from django import template
from django.template.defaultfilters import stringfilter

register = template.Library()

@register.filter(name='remove')
@stringfilter
def remove(value, arg):
return value.replace(arg, '')

@register.filter
def lower(value):
return value.lower()

然而我们在Template中使用自定义的过滤器时,要先加这一段代码:

1
{% load myfilter %}

Django Template自定义标签

Django中的自定义标签要比自定义过滤器复杂不少,在开始之前,我们先大概了解下Django Template的编译过程,当Django去编译一个Template时,它会把内容转成一个个django.template.Node实例,每个实例都有一个render方法,因此一个Template可以看做是一个Node的列表。当一个模板被渲染时,Template中Node实例的render方法会被调用,最终的返回会被合并。所以我们自定义标签就需要去实现我们自己的Node类和render方法。
我们动手来写一个标签,调用方式如下:

1
{% current_time "%Y-%m-%D %I:%M %p" %}

首先还是要写这几句代码用来注册(我们把这部分代码放入自定义myfilter.py中,这两行就可以省略了):

1
2
3
from django import template

register = template.Library()

我们定义一个Node类:

1
2
3
4
5
6
7
8
class CurrentTimeNode(template.Node):
def __init__(self, format_string):
self.format_string = str(format_string)

def render(self, context):
now = datetime.datetime.now()
# 返回的是格式化后的时间字符串
return now.strftime(self.format_string)

然后定义一个编译函数用来创建Node实例:

1
2
3
4
5
6
7
8
9
def do_current_time(parser, token):
try:
tag_name, format_string = token.split_contents()
# tag_name = 'current_time'
# format_string = '"%Y-%m-%D %I:%M %p"'
except ValueError:
msg = '%r tag requires a single argument' % token.split_contents()[0]
raise template.TemplateSyntaxError(msg)
return CurrentTimeNode(format_string[1:-1]) # 去除两个'"'

每一个标签的编译函数都需要parser和token两个函数,parser是模板分析对象,token为分析后的内容,可以直接使用。token.contents为标签的内容,token.split_contents会按空格分割token的内容,返回一个tuple,不过会保留‘“’,使用时要注意。其中token.split_contents()[0]可以得到标签名。

然后我们要注册一下标签:

1
2
3
@register.tag(name="current_time") 仅限Python2.4以上

register.tag('current_time', do_current_time)

Template中使用时依然要加上:

1
{% load myfilter %}

自定义标签还有一些更高级的用法,我们在后续的blog中再讲。。

参考

官方文档:https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/