每一个Web框架都需要一种很便利的方法用于动态生成HTML页面。 最常见的做法是使用模板。
模板包含所需HTML页面的静态部分,以及一些特殊的模版语法,用于将动态内容插入静态部分。
说白了,模板层就是如何往HTML文件中填入动态内容的系统。
模板的设计实现了业务逻辑view与显示内容template的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用。
1.配置引擎
Django可以配置一个或多个模板引擎(语言),也可以不用引擎。
Django自带一个称为DTL(Django Template Language )的模板语言,以及另外一种流行的Jinja2语言(需要提前安装,pip install Jinja2)
如果使用默认引擎,也不用配置什么,详细的配置,这里不做赘述,可以参考这个博客http://www.liujiangblog.com/course/django/144,或官方文档(英文允许的话)。
2模板语法—变量
变量看起来就像是这样: {{ variable }}。
当模版引擎遇到一个变量,它将从上下文context中获取这个变量的值,然后用值替换掉它本身。
变量的命名包括任何字母数字以及下划线("_")的组合。点(".")也有可能会在变量名中出现,不过它有特殊的含义。最重要的是,变量名称中不能有空格或标点符号。
当模版系统遇到点("."),它将以这样的顺序查询这个圆点具体代表的功能:
- 字典查询(用于字典)
- 属性或方法查询(用于类)
- 数字索引查询(用于列表或元组)
如果你使用的变量不存在,模版系统将插入string_if_invalid
选项的值,默认设置为''(空字符串)。
示例:
视图views.py
class Book(object):
def __init__(self, title):
self.title=title
def test(request):
nums=[10, 11, 12]
info={'name':'Eric'}
book=Book('长得帅如何与人相处')
context={'nums':nums, 'info':info, 'book':'book'}
return render(request, 'test.html', context)
模板test.html
<body> <h3>书的编号:{{ nums.0 }}</h3> <h3>书的作者:{{info.name }}</h3> <h3>书的名字:{{ book.title }}</h3> </body>
注意:句点符也可以用来引用对象的方法(无参数方法):
<h3>作者:{{ info.name.upper }}</h3>
3过滤器
因为过滤器紧挨着变量,所以先说过滤器
过滤器看起来是这样的:{{ name|lower }}
。使用管道符号(|
)来应用过滤器。该过滤器将文本转换成小写。
一些过滤器带有参数,过滤器参数包含空格的话,必须用引号包起来。例如,使用逗号和空格去连接一个列表中的元素,你需要使用{{ list|join:", " }}
。
django内置的过滤器有很多,列举几个常用的:
default
如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。
{{ value|default:"nothing" }}
length
返回值的长度。它对字符串和列表都起作用。
{{ value|length }} # 如果value是['a', 'b', 'c', 'd'],那么输出4。
slice
返回列表的一部分。也就是切片,与Python的列表切片相同的语法。
{{ some_list|slice:":2" }} # 如果some_list是['a', 'b', 'c'] ,输出将为['a', 'b']。
truncatechars
如果字符串包含的字符总个数多于指定的字符数量,那么会被截断掉后面的部分。截断的字符串将以“...”结尾。
{{ value|truncatechars:9 }} # 如果value是Joel is a slug,输出为Joel i...。
date
根据给定格式对一个日期变量进行格式化。
# 如果 value=datetime.datetime.now() {{ value|date:"Y-m-d" }}
safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。比如:
value="<a href="">点击</a>" {{ value|safe}}
关于自动转义,我们后面再说。
4.标签
标签看起来像是这样的: {% tag %}
。
标签比变量复杂得多,有些用于在输出中创建文本,有些用于控制循环或判断逻辑,有些用于加载外部信息到模板中供以后的变量使用。
一些标签需要开始和结束标签(即 {% 标签 %} ... 标签 内容 ... {% ENDTAG %}
)。
同样,列举几个常用的:
for标签
# 循环对象中的每一个元素 <ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul> # 如果循环对象points的每个元素都是(x,y)这样的二元元组,可以像以下面一样输出 {% for x, y in points %} There is a point at {{ x }},{{ y }} {% endfor %} 要访问一个字典中的键值,这个方法同样有用: {% for key, value in data.items %} {{ key }}: {{ value }} {% endfor %}
下面是Django为for标签内置的一些属性,可以当作变量一样使用{{ }}
在模版中使用。
- forloop.counter:循环的当前索引值,从1开始计数;常用于生成一个表格或者列表的序号!
- forloop.counter0:循环的当前索引值,从0开始计数;
- forloop.revcounter: 循环结束的次数(从1开始)
- forloop.revcounter0 循环结束的次数(从0开始)
- forloop.first:判断当前是否循环的第一次,是的话,该变量的值为True。我们经常要为第一行加点特殊的对待,就用得上这个判断了,结合if。
- forloop.last:如果这是最后一次循环,则为真
- forloop.parentloop:对于嵌套循环,返回父循环所在的循环次数。某些场景下,这是个大杀器,能解决你很多头疼的问题。
for ... empty
for标签带有一个可选的{% empty %}
从句,以便在循环对象是空的或者没有被找到时,可以有所操作和提示。
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% empty %} <li>Sorry, no athletes in this list.</li> {% endfor %} </ul>
if标签
{% if %}
会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的False值),这个内容块就会输出。
if标签可以使用not,and或or来测试布尔值
if标签允许使用这些操作符:==
, !=
, <
, >
, <=
, >=
, in
, not in
, is
, is not
{% if num > 100 or num < 0 %} <p>无效</p> {% elif num > 80 and num < 100 %} <p>优秀</p> {% else %} <p>凑活吧</p> {% endif %}
include
加载指定的模板并以标签内的参数渲染。这是一种引入别的模板的方法,include类似Python的import。
{% include "foo/bar.html" %} # 将子模版渲染并嵌入当前HTML中
url
这是路由在模板层面反向解析用到的标签,返回与给定视图和可选参数匹配的绝对路径引用(不带域名的URL)
{% url 'some-url-name' v1 v2 %} # 第一个参数是url()的名字,其他参数是可选的并且以空格隔开,这些值会在URL中以参数的形式传递
如果使用urlconf的名称空间网址,通过冒号指定完全名称,如下所示:
{% url 'myapp:view-name' %}
with
使用一个简单地名字缓存一个复杂的变量,当你需要使用一个代价较大的方法(比如访问数据库)很多次的时候这是非常有用的。
{% with total=business.employees.count %} {{ total }} employee{{ total|pluralize }} {% endwith %}
csrf_token
这个标签用于跨站请求伪造保护
5.自定制标签、过滤器
Django为我们提供了自定义的机制,可以通过使用Python代码,自定义标签和过滤器来扩展模板引擎,然后使用{% load %}标签。
5.1配置准备
1.将要增加自定义标签的app在INSTALLED_APPS
中注册,否则django无法找到自定义的标签。
2.在app中新建一个templatetags
包(名字固定,不能变,只能是这个),和views.py、models.py等文件处于同一级别目录下。这是一个包!不要忘记创建__init__.py
文件以使得该目录可以作为Python的包。并在该包下创建了一个名为mytags.py的文件。
3.要在模块内自定义标签,首先,这个模块必须包含一个名为register
的变量,它是template.Library
的一个实例,所有的标签和过滤器都是在其中注册的。 所以把如下的内容放在mytags.py文件顶部:
from django import template register = template.Library()
5.2自定义过滤器
- 自定义过滤器实际上就是写一个函数
- django会将过滤器前的值传入该函数
- 函数完成后,需要进行注册register
因为第二步django已经帮我们完成,所以我们实际上只需要自己完成第一步和第三步
示例:定义一个自动省略字符的过滤器
# mytags.py from django import template register = template.Library() @register.filter(name='truncate_char') # 注册过滤器的装饰器,name属性可以省略 def truncate_char(value): # 定义过滤器的函数 if len(value) > 20: return value[0:20]+'...' else: return value
然后在模板页面装载使用
{% load mytags %} 装载自定义过滤器 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ '超过20个字符后,将后面的字符省略,没超过20个字符,则可以显示全部'|truncate_chars }} </body> </html>
5.3自定义标签
自定义标签相对于自定义过滤器来说要复杂很多,因为自定义标签可以做任何事情!
自定义标签分为很多类型
- 简单标签 Simple tags
- 内含标签 Inclusion tags
- 分配标签 Assignment tags
5.3.1简单标签
import datetime from django import template register = template.Library() @register.simple_tag def current_time(format_string): # 传入一个时间格式 return datetime.datetime.now().strftime(format_string) # 返回当前时间
Library.simple_tag(takes_context=True),takes_context=True参数可以让我们访问模板的当前环境上下文,即将当前环境上下文中的参数和值作为字典传入函数中的一个名为context的参数
@register.simple_tag(takes_context=True) def current_time(context,fromat_string): timezone = context['timezone'] # 接收上下文参数 return your_get_current_time_method(timezone, format_string)
当使用take_context=True时,函数的第一个参数必需为context。也可以使用name参数对函数进行重命名。
5.3.2内含标签
这种类型的标签可以被其他模板进行渲染,然后将渲染结果输出
Library.inclusion_tag()支持take_context=True,用法类似Library.simple_tag()
from django import template register = template.Library() @register.inclusion_tag('result.html') def tset(): a=['first', 'second', 'third'] return {'choices': a}
result.html
<ul> {% for choice in choices %} <li> {{ choice }} </li> {% endfor %} </ul>
test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% load mytags %} # 装载自定义模块 {% test %} # 导入标签 </body> </html>
5.3.3分配标签
类似于简单标签,但并不会输出结果,可以使用 as 关键字将结果赋给一个参数。
@register.assignment_tag def get_current_time(format_string): return datetime.datetime.now().strftime(format_string)
{% get_current_time "%Y-%m-%d %I:%M %p" as the_time %} <p>The time is {{ the_time }}.</p>
6.模板继承
Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让我们创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。
模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量。
父模板
如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。
block标签:用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同。 为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。
{% block 名称 %} 预留区域,可以编写默认内容,也可以没有默认内容 {% endblock %}
子模板
extends标签:继承,写在子模板文件的第一行。
{% extends "父模板路径" %}
子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。
填充父模板中指定名称的预留区域。
{% block 名称 %} 实际填充内容 {{ block.super }} # 用于保留父模板中block的内容 {% endblock 名称 %}
示例
父模板:base.html
<html> <head> <title>{{title}}</title> </head> <body> <h2>这是头</h2> <hr> {%block content%} 这是内容,有默认值 {%endblock content%} <hr> {%block foot%} {%endblock foot%} <hr> <h2>这是尾</h2> </body> </html>
子模板:test.html
{% extends "base.html" %}
{% block content %}
两个黄鹂鸣翠柳
我还没有女朋友
{{ block.super }}
{% endblock content %}
{% block foot %}
垂死病中惊坐起
笑问客从何处来
{% endblock foot %}
那么子模板的输出效果就是:
<html> <head> <title>{{title}}</title> </head> <body> <h2>这是头</h2> <hr> 两个黄鹂鸣翠柳 我还没有女朋友 这是内容,有默认值 <hr> 垂死病中惊坐起 笑问客从何处来 <hr> <h2>这是尾</h2> </body> </html>
注意:
- 如果你在模版中使用
{% extends %}
标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。 - 在base模版中设置越多的
{% block %}
标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。 - 如果你发现你自己在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个
{% block %}
中 - 不能在一个模版中定义多个相同名字的 block 标签。