前言:当咱们想在页面上给客户端返回一个当前时间,一些初学者可能会很天然的想到用占位符,字符串拼接来达到咱们想要的效果,可是这样作会有一个问题,HTML被直接硬编码在 Python代码之中。css
1
2
3
4
|
def
current_datetime(request):
now
=
datetime.datetime.now()
html
=
"<html><body>It is now %s.</body></html>"
%
now
return
HttpResponse(html)
|
尽管这种技术便于解释视图是如何工做的,但直接将HTML硬编码到你的视图里却并非一个好主意。 让咱们来看一下为何:html
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改每每比底层 Python 代码的修改要频繁得多,所以若是能够在不进行 Python 代码修改的状况下变动设计,那将会方便得多。python
Python 代码编写和 HTML 设计是两项不一样的工做,大多数专业的网站开发环境都将他们分配给不一样的人员(甚至不一样部门)来完成。 设计者和HTML/CSS的编码人员不该该被要求去编辑Python的代码来完成他们的工做。程序员
程序员编写 Python代码和设计人员制做模板两项工做同时进行的效率是最高的,远胜于让一我的等待另外一我的完成对某个既包含 Python又包含 HTML 的文件的编辑工做。数据库
基于这些缘由,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 咱们可使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。django
python的模板:HTML代码+模板语法缓存
模版包括在使用时会被值替换掉的 变量,和控制模版逻辑的 标签。安全
def current_time(req): # ================================原始的视图函数 # import datetime # now=datetime.datetime.now() # html="<html><body>如今时刻:<h1>%s.</h1></body></html>" %now # ================================django模板修改的视图函数 # from django.template import Template,Context # now=datetime.datetime.now() # t=Template('<html><body>如今时刻是:<h1>{{current_date}}</h1></body></html>') # #t=get_template('current_datetime.html') # c=Context({'current_date':str(now)}) # html=t.render(c) # # return HttpResponse(html) #另外一种写法(推荐) import datetime now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':str(now)[:19]})
在 Django 模板中遍历复杂数据结构的关键是句点字符 .数据结构
语法: app
语法:
1
|
{{var_name}}
|
views:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def
index(request):
import
datetime
s
=
"hello"
l
=
[
111
,
222
,
333
]
# 列表
dic
=
{
"name"
:
"yuan"
,
"age"
:
18
}
# 字典
date
=
datetime.date(
1993
,
5
,
2
)
# 日期对象
class
Person(
object
):
def
__init__(
self
,name):
self
.name
=
name
person_yuan
=
Person(
"yuan"
)
# 自定义类对象
person_egon
=
Person(
"egon"
)
person_alex
=
Person(
"alex"
)
person_list
=
[person_yuan,person_egon,person_alex]
return
render(request,
"index.html"
,{
"l"
:l,
"dic"
:dic,
"date"
:date,
"person_list"
:person_list})
# return render(request,"index.html",locals())
|
template:
1
2
3
4
5
6
|
<h4>{{s}}<
/
h4>
<h4>列表:{{ l.
0
}}<
/
h4>
<h4>列表:{{ l.
2
}}<
/
h4>
<h4>字典:{{ dic.name }}<
/
h4>
<h4>日期:{{ date.year }}<
/
h4>
<h4>类对象列表:{{ person_list.
0.name
}}<
/
h4>
|
注意:句点符也能够用来引用对象的方法(无参数方法)。
1
|
<h4>字典:{{ dic.name.upper }}<
/
h4>
|
语法:
1
|
{{obj|filter__name:param}}
|
default
若是一个变量是false或者为空,使用给定的默认值。不然,使用变量的值。例如:
1
|
{{ value|default:
"nothing"
}}
|
返回值的长度。它对字符串和列表都起做用。例如:
1
|
{{ value|length }}
|
若是 value 是 ['a', 'b', 'c', 'd'],那么输出是 4。
filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如‘13 KB’,‘4.1 MB’,‘102 bytes’, 等等)。例如:
1
|
{{ value|filesizeformat }}
|
若是 value
是 123456789,输出将会是 117.7 MB
。
若是 value=datetime.datetime.now(),将返回当前时间的年-月-日格式
1
|
{{ value|date:
"Y-m-d"
}}
|
若是 value="hello world",则结果为ello worl (也是顾头不顾尾)
1
|
{{ value|
slice
:
"2:-1"
}}
|
若是字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。(truncatewords则是按单词数目截断(单词与单词间按空格算),用法相同)
参数:要截断的字符数
例如:
1
|
{{ value|truncatechars:
3
}}
|
若是value是“Joel是a,输出将为“Joe...”。
Django的模板中会对HTML标签和JS等语法标签进行自动转义,缘由显而易见,这样是为了安全。可是有的时候咱们可能不但愿这些HTML元素被转义,好比咱们作一个内容管理系统,后台添加的文章中是通过修饰的,这些修饰多是经过一个相似于FCKeditor编辑加注了HTML修饰符的文本,若是自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,若是是一个单独的变量咱们能够经过过滤器“|safe”的方式告诉Django这段代码是安全的没必要转义。好比:
1
|
value
=
"<a href="
">点击</a>"
|
1
|
{{ value|safe}}
|
这里简单介绍一些经常使用的模板的过滤器,更多详见
标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中建立文本,一些经过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。
一些标签须要开始和结束标签 (例如 {% tag %} ...
标签 内容 ... {% endtag %})。
遍历每个元素:
{% for person in person_list %} <p>{{ person.name }}</p> {% endfor %}
能够利用{% for obj in list reversed %}反向完成循环。
遍历一个字典:
{% for key,val in dic.items %} <p>{{ key }}:{{ val }}</p> {% endfor %}
注:循环序号能够经过{{forloop}}显示
forloop.counter #默认第一个循环的序号为1,升序 forloop.counter0 # 默认第一个循环的序号为0,升序 forloop.revcounter #循环序号为降序,最后一个为1 forloop.revcounter0 #循环序号为降序,最后一个为0 forloop.first #第一个为True,其他为False forloop.last #最后一个为True,其他为False
for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,能够有所操做。
{% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>sorry,no person here</p> {% endfor %}
{% if %}会对一个变量求值,若是它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。
{% if num > 100 or num < 0 %} <p>无效</p> {% elif num > 80 and num < 100 %} <p>优秀</p> {% else %} <p>凑活吧</p> {% endif %}
==, !=, >=, <=, >, < 这些比较均可以在模板中使用
(注意:比较符号先后必须有至少一个空格!)
and, or, not, in, not in 也能够在模板中使用
使用一个简单地名字缓存一个复杂的变量,当你须要使用一个“昂贵的”方法(好比访问数据库)不少次的时候是很是有用的
例如:
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
这个标签用于跨站请求伪造保护
<form action="/login/" method="post"> {% csrf_token %} #加上这个才能正常POST请求 <p><input type="text" name="user"></p> <input type="submit"> </form>
一些有趣的用法:
.获取当前用户:
{{ request.user }}
若是登录就显示内容,不登录就不显示内容:
{% if request.user.is_authenticated %}
{{ request.user.username }},您好!
{% else %}
请登录,这里放登录连接
{% endif %}
一、在settings中的INSTALLED_APPS配置当前app(即加上当前app的名字,如‘app01’),否则django没法找到自定义的simple_tag.
二、在app中建立templatetags模块(模块名只能是templatetags)
三、建立任意 .py 文件,如:my_tags.py
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变
####################以上行是固定不变要写的#############################
######################定义过滤器#################### @register.filter def filter_multi(v1,v2): return v1 * v2
######################定义标签##################### <br> @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 <br> @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
四、在使用自定义simple_tag和filter的html文件中导入以前建立的 my_tags.py
1
|
{
%
load my_tags
%
}
|
五、使用simple_tag和filter(如何调用)
-------------模板页面.html--------------------
过滤器: {{var|filter_name:参数}} # 参数只能是一个
标签: {% simple_tag 参数1 参数2 ... %} #参数不限
{% load my_tags %} # num=12 {{ num|filter_multi:2 }} #24 #参数只能有一个 {{ num|filter_multi:"[22,333,4444]" }} {% simple_tag_multi 2 5 %} #参数不限,但不能放在if for语句中 {% simple_tag_multi num 5 %}
注意:filter能够用在if等语句后,simple_tag不能够
1
2
3
|
{
%
if
num|filter_multi:
30
>
100
%
}
{{ num|filter_multi:
30
}}
{
%
endif
%
}
|
对比过滤器与自定义标签的使用:
过滤器函数的参数只能有两个,自定义标签无参数限制
过滤器能够与逻辑标签共同使用,好比if标签。自定义标签不能够。
Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可让您建立一个基本的“骨架”模版,它包含您站点中的所有元素,而且能够定义可以被子模版覆盖的 blocks 。
经过从下面这个例子开始,能够容易的理解模版继承,首先咱们写一个模板:
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>
这个模版,咱们把它叫做 base.html
, 它定义了一个能够用于两列排版页面的简单HTML骨架。“子模版”的工做是用它们的内容填充空的blocks。
在这个例子中, block
标签订义了三个能够被子模版内容填充的block。 block
告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。
接下来,咱们再写一个子模板:
{% extends "base.html" %} {% block title %}My amazing blog{% endblock %} {% block content %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %}
extends
标签是这里的关键。它告诉模版引擎,这个模版“继承”了另外一个模版。当模版系统处理这个模版时,首先,它将定位父模版——在此例中,就是“base.html”。
那时,模版引擎将注意到 base.html
中的三个 block
标签,并用子模版中的内容来替换这些block。根据 blog_entries
的值,子模板输出的内容会是下面的内容:
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>My amazing blog</title> </head> <body> <div id="sidebar"> <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> </div> <div id="content"> <h2>Entry one</h2> <p>This is my first entry.</p> <h2>Entry two</h2> <p>This is my second entry.</p> </div> </body> </html>
请注意,子模版并无定义 sidebar
block,因此系统使用了父模版中的值。父模版的 {% block %}
标签中的内容老是被用做备选内容(fallback)。
这种方式使代码获得最大程度的复用,而且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。
这里是使用继承的一些提示:
若是你在模版中使用 {% extends %}
标签,它必须是模版中的第一个标签。其余的任何状况下,模版继承都将没法工做。
在base模版中设置越多的 {% block %}
标签越好。请记住,子模版没必要定义所有父模版中的blocks,因此,你能够在大多数blocks中填充合理的默认内容,而后,只定义你须要的那一个。多一点钩子总比少一点好。
若是你发现你本身在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %}
中。
有时候,想在父模板的基础上再添加点其余的,而不是彻底覆盖父模板的内容,那么你只须要在想要填充的块里,再加上一句 {{ block.super }},
咱们就能够把父模板里的东西给留下来,如:
父模板中的
{% block title %}My amazing site{% endblock %},
若是咱们只想子模板的结果是在后面加一个!,
则只须要在子模板中写成
{% block title %}
{{ block.super }}!
{% endblock %}
便可。
为了更好的可读性,你也能够给你的 {% endblock %}
标签一个 名字 。例如:
1
2
3
|
{
%
block content
%
}
...
{
%
endblock content
%
}
|
在大型模版中,这个方法帮你清楚的看到哪个 {% block %}
标签被关闭了。
最后,请注意您并不能在一个模版中定义多个相同名字的 block
标签。这个限制的存在是由于block标签的做用是“双向”的。这个意思是,block标签不只提供了一个坑去填,它还在 _父模版_中定义了填坑的内容。若是在一个模版中有两个名字同样的 block
标签,模版的父模版将不知道使用哪一个block的内容。
注意:模板通常放在app下的templates中,Django会自动去这个文件夹中找。但 假如咱们每一个app的templates中都有一个 index.html,当咱们在views.py中使用的时候,直接写一个 render(request, 'index.html'),Django 能不能找到当前 app 的 templates 文件夹中的 index.html 文件夹呢?(答案是不必定能,有可能找错)
Django 模板查找机制: Django 查找模板的过程是在每一个 app 的 templates 文件夹中找(而不仅是当前 app 中的代码只在当前的 app 的 templates 文件夹中找)。各个 app 的 templates 造成一个文件夹列表,Django 遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就中止,全部的都遍历完了还找不到指定的模板的时候就是 Template Not Found (过程相似于Python找包)。这样设计有利固然也有弊,有利是的地方是一个app能够用另外一个app的模板文件,弊是有可能会找错了。因此咱们使用的时候在 templates 中创建一个 app 同名的文件夹,这样就行了。
这就须要把每一个app中的 templates 文件夹中再建一个 app 的名称,仅和该app相关的模板放在 app/templates/app/ 目录下面,
例如:项目 zqxt 有两个 app,分别为 tutorial 和 tryit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
zqxt
├── tutorial
│ ├── __init__.py
│ ├── admin.py
│ ├── models.py
│ ├── templates
│ │ └── tutorial
│ │ ├── index.html
│ │ └── search.html
│ ├── tests.py
│ └── views.py
├── tryit
│ ├── __init__.py
│ ├── admin.py
│ ├── models.py
│ ├── templates
│ │ └── tryit
│ │ ├── index.html
│ │ └── poll.html
│ ├── tests.py
│ └── views.py
├── manage.py
└── zqxt
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
|
这样,使用的时候,模板就是 "tutorial/index.html" 和 "tryit/index.html" 这样有app做为名称的一部分,就不会混淆。