第五章 模板
Django作为web框架,需要一种很便利的方法去动态地生成HTML网页,因此有了模板这个概念。模板包含所需HTML的部分代码以及一些特殊语法
Django可以配置一个或多个模板引擎(甚至是0,如果不需要使用模板),模板系统有Django模板语言(Django Template Language, DTL)和Jinja2。Django模板语言是Django内置的模板语言,Jinja2是当前Python最流行的模板语言。
5.1 变量与标签
变量是模板中最基本的组成单位,模板变量是由视图函数生成的。如果变量没有被视图函数生成,那么模板引擎解析HTML时,模板变量不会显示在网页上。变量以{{ variable }}表示,variable是变量名,变量的类型可以是Python支持的数据类型,使用如下:
# variable为字符串类型或整型,如variable = "Python"
{{ variable }}
# 输出Python
# variable为字典或数据对象,通过点号(.)来访问其属性值
# 如variable = {"name": "Lily", "info": {"home": "BeiJing", "homeplace": "ShangHai"}}
{{ variable.name }}
# 输出Lily
{{ variable.info.home }}
# 输出BeiJing
以MyDjango为例,抽取模板index.html的部分代码,代码如下:
<head> <title>{{ title }}</title> <meta charset="utf-8"> {% load staticfiles %} <link rel="stylesheet" type="text/css" href="{% static "css/hw_index.css" %}" > <link rel="icon" href="{% static "img/hw.ico" %}"> <script src="{% static "js/hw_index.js" %}"></script> </head> <ul id="cate_box" class="lf"> {% for type in type_list %} <li> <h3><a href="#">{{ type.type }}</a></h3> <p> {% for name in name_list %} {% if name.type == type.type %} <span>{{ name.name }}</span> {% endif %} {% endfor %} </p> </li> {% endfor %} </ul>
上述代码分别使用模板的变量和标签,代码说明如下:
1、{{ title }}代表模板变量,从变量名title可以,变量的数据类型是字符串类型或整型。
2、{% load staticfiles %}是模板的内置标签,load标签用于导入静态资源信息。
3、{% static "css/hw_index.css" %}是模板是内置标签,static标签用于读取静态资源的文件内容。
4、{% for type in type_list %}是for便利标签,将变量进行遍历输出。
5、{% if name.type == type.type %}是if判断标签,主要对变量进行判断处理。
6、{{ type.type }}代表变量type_list的某个属性。
从上面的例子可以看到,模板的变量需要和标签相互结合使用。模板的标签就如Python里面的函数和方法,Django常用的内置标签说明如下:
标签 | 描述 |
{% for %} | 遍历输出变量的内容,变量类型应为列表或数据对象 |
{% if %} | 对变量进行条件判断 |
{% csrf_token %} |
生成csrf_token的标签,用于防护跨站请求伪造攻击 |
{% url %} | 引用路由配置的地址,生成相应的URL地址 |
{% with %} |
将变量名重新命名 |
{% load %} | 加载导入Django的标签库 |
{% static %} | 读取静态资源的文件内容 |
{% extends %} | 模板继承,xxx为模板文件名,使当前模板继承xxx模板 |
{% block xxx %} | 重写父类模板的代码 |
在上述常用标签中,每个标签的使用方法都是各不相同的。使用如下:
# for标签,支持嵌套,myList可以是列表或某个对象 # item可自定义命名,{% endfor %}代表循环区域终止符,代表这个区域的代码由标签for循环输出 {% for item in myList %} {{ item }} {% endfor %} # if标签,支持嵌套,判断条件符必须与变量之间使用空格隔开,否则程序会抛出异常 # {% endif %}与{% endfor %}的作用是相同的 {% if name == "Lily" %} {{ name }} {% elif name == "Lucy" %} {{ name }} {% else %} {{ name }} {% endif %} # url标签 # 生成不带变量的URL地址 # 相关的路由地址: path('', views.index, name='index') # 字符串index是URL的参数name的值 <a href="{% url 'index' %}" target="_blank">首页</a> # 生成带变量的URL地址 # 相关的路由地址:path('search/<int:page>.html', views.search, name='search') # 字符串search是URL的参数name的值,1是URL的变量page的值 <a href="{% url 'search' 1 %}" target="_blank">第一页</a> # total标签 {% with total = products_total %} {{ total }} {% endwith %} # load标签,导入静态文件标签库staticfiles,staticfiles来自settings.py的INSTALLED_APPS {% load staticfiles %} # static标签,来自静态文件标签库staticfiles {% static "css/hw_index.css" %}
在for标签中,模板还提供一些特殊的变量来获取for标签的循环信息,变量说明如下:
变量 | 描述 |
forloop.counter | 获取当前循环的索引,从1开始计算 |
forloop.counter() | 获取当前循环的索引,从0开始计算 |
forloop.revcounter | 索引从最大数开始递减,直到索引到1位置 |
forloop.revcounter() | 索引从最大数开始递减,直到索引到0位置 |
forloop.first | 当遍历的元素为第一项时为真 |
forloop.last | 当遍历的元素为最后一项时为真 |
forloop.parentloop | 在嵌套的for循环中,获取上层for循环的forloop |
上述变量来自于forloop对象,该对象是模板引擎解析for标签时所生成的。下面是使用方法:
{% for name in name_list %} {% if forloop.counter == 1 %} <span>这是第一次循环</span> {% elif forloop.last %} <span>这是最后一次循环</span> {% else %} <span>本次循环次数为: {{ forloop.counter }}</span> {% endif %} {% endfor %}
5.2 模板继承
模板继承是通过模板标签来实现的,其作用是将多个HTML模板的共同代码集中在一个新的HTML模板中,然用户各个模板可以直接调用新的HTML模板,从而生成HTML网页,这样可以减少模板之间重复的代码。代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <a href="{% url 'index' %}" target="_blank">首页</a> <h1>Hello Django</h1> </body> </html>
上述代码是一个完整的HTML模板,一个完整的模板有<html>和<body>两大部分,其中<html>部分在大多数情况下都是相同的,因此可以将<html>部分写道共用模板中,将共用模板命名为base.html,代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> {% block body %}{% endblock %} </body> </html>
在base.html的代码中可以看到,<body>里的内容变为{% block body %}{% endblock %},block标签相当于一个函数,body是对该函数的命名,开发者可自行命名。在一个模板中可以添加多个block标签,只要每个block标签的命名各不相同即可。接着在模板index.html中调用共用模板base.html,代码如下:
{% extends "base.html" %} {% block body %} <a href="{% url 'index' %}" target="_blank">首页</a> <he>Hello Django</he> {% endblock %}
模板index.html调用共用模板base.html的实质是有模板继承实现的,调用步骤如下:
1、在模板index.html中使用{% extends "base.html" %}来继承模板base.html的代码。
2、由标签{% block body %}在继承模板的基础上实现自定义模板的内容。
3、由{% endblock %}结束block标签。
从index.html看到,模板继承与Python的类继承的原理是一致的,通过继承的方式使其具有父类的功能和属性,然后以重写的方式实现各种开发需求。
5.3 自定义过滤器
过滤器主要是对变量的内容进行处理,如替换、反序和转义等。通过过滤器处理变量可以将变量的数据格式和内容转换为我们想要的效果,而且相应减少视图函数的代码量。过滤器的使用方法如下:
{{ variable | filter }}
模板引擎解析带过滤器的变量时,首先过滤器filter处理变量variable,然后将处理后的变量显示在网页上。其中,variable代表模板变量,管道符号" | "代表变量使用过滤器,filter代表某个过滤器。变量可以支持多个过滤器同时使用,例如:
{{ variable | filter | lower }}
在使用的过程中,有些过滤器还可以传入参数,但仅支持一个参数的传入。带参过滤器的使用方法如下:
{{ variable | date:"D d M Y" }}
Django为开发者内置过滤器如下:
内置过滤器 | 使用形式 | 说明 |
add | {{ value | add: "2" }} | 将value的值增加2 |
addslashes | {{ value | addslashes }} | 在value中的引号前增加反斜线 |
capfirst | {{ value | capfirst }} | value的第一个字符转化成大写形式 |
cut | {{ value | cut:arg }} | 从value中删除所有arg的值。如果value是"String with spaces", arg是" ",那么输出的是"Stringwithspaces" |
date | {{ value | date:"D d M Y" }} | 将日期格式数据按照给定的格式输出 |
default | {{ value | default:"nothing" }} | 如果value的意义是False,那么输出值为过滤器设定的默认值 |
default_if_none | {{ value | default_if_none:"nothing }} | 如果value的意义是None,那么输出值为过滤器设定的默认值 |
dictsort | {{ value | dictsort:"name" }} | 如果value的值是一个列表,里面的元素是字典,那么返回值按照每个字典的关键字排序 |
dictsortreversed | {{ value | dictsortreversed:arg }} | 如果value的值是一个列表,里面的元素是字典,每个字典的关键字反序排行 |
divisibleby | {{ value | divisibleby:arg }} | 如果value能够被arg整除,那么返回值将是True |
escape | {{ value | escape }} | 控制HTML转义,替换value中的某些HTML特殊字符 |
escapejs | {{ value | escapejs}} | 替换value中的某些字符,以适应JavaScript和JSON格式 |
filesizeformat | {{ value | filesizeformat }} | 格式化value,使其成为易读的文件大小,例如13KB、4.1MB等 |
first | {{ value | first }} | 返回列表中的第一个Item,例如,如果value是列表['a','b','c'],那么输出将是'a' |
floatformat | {{ value | floatformat }}{{ value | floatformat:arg }} | 对数据进行四舍五入处理,参数arg是保留小数位,可以是正数或负数,如{{ value | floatformat:"2" }}是保留两位小数。若无参数arg,默认保留1位小数,如{{ value | floatformat }} |
get_digit | {{ value | get_digit:"arg" }} | 如果value是123456789,arg是2,那么输出的是8 |
iriencode | {{ value | iriencode }} | 如果value中有非ASCII字符,那么将其转化成URL中适合的编码 |
join | {{ value | join:"arg"}} |
使用指定的字符串连接一个list,作用如同Python的str.join(list) |
last | {{ value | last }} | 返回列表中的最后一个Item |
length | {{ value | length }} | 返回value的长度 |
length_is | {{ value | length_is:"arg" }} | 如果value的长度等于arg,例如:value是['a','b','c'],arg是3,那么返回True |
linebreaks | {{ value | linebreaks }} | value中的" "将被<br/>替代,并且将整个value使用<p>包围起来,从而适合HTML的格式 |
linebreaksbr | {{ value | linebreaksbr }} | value中的" "将被<br/>替代 |
timeuntil | {{ value | timeuntil }} | 返回value距离当前日期的天数和小时数 |
linenumbers | {{ value | linenumbers }} | 为显示的文本添加行数 |
ljust | {{ value | ljust }} | 以左对齐方式显示value |
center | {{ value | center }} | 以居中对齐方式显示value |
rjust | {{ value | rjust }} | 以右对齐方式显示value |
lower | {{ value | lower }} | 将一个字符串转换成小写形式 |
make_list | {{ value | make_list }} | 将value转换成list。例如value是Joel,输出[u'J',u'o',u'e',u'l'];如果value是123,那么输出是[1,2,3] |
pluralize |
{{ value | pluralize }}或 {{ value | pluralize:" es " }} 或 {{ value | pluralize:"y,ies }} |
将value返回英文复数形式 |
random | {{ value | random }} | 从给定的list中返回一个任意的Item |
removetags | {{ value | removetags:"tag1 tag2 tag3..." }} | 删除value中tag1,tag2...的标签 |
safe | {{ value | safe }} | 关闭HTML转义,告诉Django这段代码是安全的,不必转义 |
safeseq | {{ value | safeseq }} | 与上述safe基本相同,但有一点不同;safe针对字符串,而safeseq针对多个字符串组成的sequence |
slice | {{ some_list | slice:":2" }} | 与Python语法中的slice相同,":2"表示截取前两个字符,此过滤器可用于中文或英文 |
slugify | {{ value | slugify }} | 将value转换成小写形式,同时删除所有分单词字符,并将空格变成横线。例如:value是Joel is a slug,那么输出的将是joel-is-a-slug |
striptags | {{ value | striptags }} | 删除value中的所有HTML标签 |
time | {{ value | time:"H:i" }}或{{ value | time }} | 格式化时间输出,如果time后面没有格式化参数,那么输出按照默认设置的进行 |
truncatewords | {{ value | truncatewords:2 }} | 将value进行单词截取处理,参数2代表截取前两个单词,此过滤器值可用于英文截取。如value是Joel is a slug那么输出将是:Joel is |
upper | {{ value | upper }} | 转换一个字符串为大写形式 |
urlencode | {{ value | urlencode }} | 将字符串进行URLEncode处理 |
urlize | {{ value | urlize }} | 将一个字符串中的URL转换成可点击的形式。如果value是Check out www.baidu.com,那么输出的将是:Check out <a href="http://www.baidu.com">www.baidu.com</a> |
wordcount | {{ value | wordcount }} | 返回字符串中单词的数目 |
wordwrap | {{ value | wordwrap:5 }} | 按照指定长度的分割字符串 |
timesince | {{ value | timesince:arg }} | 返回参数arg到value的天数和小时数。如果arg是一个日期实例,表示2006-06-01午夜,而value表示2006-06-01早上8点,那么输出结果返回"8 hours" |
在实际开发中,如果内置过滤器的功能不太适合实际开发需求,我们可以通过自定义过滤器来解决问题。首先在MyDjango中添加文件和文件夹,如下图:
从图中看到,在MyDjango项目中添加了user_defined文件夹,在其文件夹下又分别添加了templatetags文件夹和__init__.py文件。templatetags用于存放自定义过滤器的代码文件,该文件夹也可以存放在项目的APP中,但必须注意的是,文件夹的命名必须为templatetags,否则Django在运行的时候无法识别自定义过滤器。最后在templatetags文件夹下创建myfilter.py文件,该文件是编写自定义过滤器的实现代码。
完成过滤器的目录搭建,接着是配置过滤器的信息,在配置文件settings.py的INSTALLED_APPS里面添加user_defined。当项目启动时,Django会从INSTALLED_APPS的配置中查找过滤器,若过滤器设置在index的目录下,则只需在INSTALLED_APPS中配置index即可,如下:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'index', 'user_defined', ]
完成上述两个环境配置后,下一步是编写自定义过滤器的实现代码,在myfilter.py中添加以下代码:
from django import template # 注册过滤器 register = template.Library() # 声明并定义过滤器 @register.filter def myreplace(value, agrs): oldValue = agrs.split(':')[0] newValue = agrs.split(':')[1] return value.replace(oldValue, newValue)
上述代码用于实现HTML模板的字符串替换功能,与Python的replace函数相同,过滤器说明如下:
1、首先导入模板功能template,通过template声明Library对象,将对象赋值给变量register,这一过程称为注册过滤器。
2、过滤器以函数的形式实现,在函数前使用register.filter装饰器来表示该函数是一个过滤器,函数名可自行命名。
3、函数参数可设置一个或两个,如上述的参数分别是value和agrs,参数value是HTML模板的变量,参数agrs是过滤器函数定义的函数参数。
4、过滤器函数最后必须将处理结果返回,否则在使用过程中会出现异常信息。
最后在HTML模板中使用我们自定义的过滤器,以index.html模板的title为例,代码如下:
{% load myfilter %} <!DOCTYPE html> <html> <head> <title>{{ title|myreplace:'首页:我的首页' }}</title> <meta charset="utf-8"> {% load staticfiles %} <link rel="stylesheet" type="text/css" href="{% static "css/hw_index.css" %}" > <link rel="icon" href="{% static "img/hw.ico" %}"> <script src="{% static "js/hw_index.js" %}"></script> </head>
在HTML模板中使用自定义的过滤器可以分为两大步骤,说明如下:
1、{% load myfilter %}用于导入templatetags文件夹的myfilter.py文件中所定义的功能,用来告诉Django在那个地方可以找到自定义过滤器。
2、{{ title | myreplace:'首页:我的首页' }}把变量title含有"首页"的内容替换成"我的首页"。其中,myreplace是过滤器的函数名,”首页:我的首页"是函数参数agrs的值,函数参数value的值为模板变量title的值。运行结果如下:
5.4 本章小结
Django作为Web框架,需要一种很便利的方法去动态地生成HTML网页,因此有了模板这个概念。模板包含所需HTML的部分代码以及一些特殊的语法,特殊的语法用于描述如何将数据动态插入HTML网页中。模板语言中主要在HTML文件中编写,大概分为三类:变量/标签和过滤器。三者说明如下:
1、变量是将视图函数传递的数据作用在模板文件上,通过模板引擎将数据转换并生成HTML网页。
2、标签可以理解为代码编程里面的函数功能,常用的标签有控制循环、条件判断和模板继承等。
3、过滤器主要是对变量的数据进行处理,如替换、反序和转义等。
模板继承与Python的类继承的原理是一致的,通过继承的方式使其具有父类的功能和属性,然后以重写的方式实现各种开发需求。模板继承的实现步骤如下:
1、在模板index.html中使用{% extends "base.html" %}来继承模板base.html的代码。
2、由标签{% block body %}在继承模板的基础上实现自定义模板的内容
3、由{% endblock %}结束block标签
过滤器主要是对变量的内容进行处理,如替换、反序和转义等。通过过滤器处理变量可以将变量的数据格式和内容转化为我们想要的效果,而且想要减少视图函数的代码量。过滤器的使用方法如下:
{{ variable | filter }}
模板引擎解析带过滤器的变量时,首先过滤器filter处理变量variable,然后将处理后的变量显示在网页上。其中,variable代表模板变量,管道符号"|"代表变量使用过滤器,filter代表某个过滤器。变量可以支持多个过滤器同时使用,例如:
{{ variable | filter | lower }}
在使用的过程中,有些过滤器还可以传入参数,但仅支持一个参数的传入。带参过滤器的使用方法如下:
{{ variable | date:"D d M Y" }}