在之前的程序中,我们直接生成一个字符串,作为http回复,返回给客户端。这一过程中使用了django.http.HttpResponse()。
在这样的一种回复生成过程中,我们实际上将数据和视图的格式混合了到上面的字符串中。看似方便,却为我们的管理带来困难。想像一个成熟的网站,其显示格式会有许多重复的地方。如果可以把数据和视图格式分离,就可以重复使用同一视图格式了。
Django中自带的模板系统,可以将视图格式分离出来,作为模板使用。这样,不但视图可以容易修改,程序也会显得美观大方。
“她是我心中最美的人”,姜戈对德国人说。
我们拿一个独立的templay.html文件作为模板。它放在templates/west/文件夹下。文件系统的结构现在是:
1
2
3
4
5
|
mysite
/
├──
mysite
├──
templates
│
└──
west
└──
west
|
templay.html文件的内容是:
1
|
h1
>
{
{
label
}
}
h1
>
|
可以看到,这个文件中,有一个奇怪的双括号包起来的陌生人。这就是我们未来数据要出现的地方。而相关的格式控制,即
我们需要向Django说明模板文件的搜索路径,修改mysite/settings.py,添加:
1
2
3
4
|
# Template dir
TEMPLATE_DIRS
=
(
os
.
path
.
join
(
BASE_DIR
,
'templates/west/'
)
,
)
|
如果还有其它的路径用于存放模板,可以增加该元组中的元素,以便Django可以找到需要的模板。
我们现在修改west/views.py,增加一个新的对象,用于向模板提交数据:
1
2
3
4
5
6
7
8
9
|
# -*- coding: utf-8 -*-
#from django.http import HttpResponse
from
django
.
shortcuts
import
render
def
templay
(
request
)
:
context
=
{
}
context
[
'label'
]
=
'Hello World!'
return
render
(
request
,
'templay.html'
,
context
)
|
可以看到,我们这里使用render来替代之前使用的HttpResponse。render还使用了一个词典context作为参数。这就是我们的数据。
context中元素的键值为’label’,正对应刚才的“陌生人”的名字。这样,该context中的‘label’元素值,就会填上模板里的坑,构成一个完整的http回复。
作为上节内容的一个小练习,自行修改west/urls.py,让http://127.0.0.1:8000/west/templay的URL请求可以找到相应的view对象。
访问http://127.0.0.1:8000/west/templay,可以看到页面:
“我给你讲个故事吧,勇士拯救公主的故事”,德国人说。
再来回顾一下整个流程。west/views.py中的templay()在返回时,将环境数据context传递给模板templay.html。Django根据context元素中的键值,将相应数据放入到模板中的对应位置,生成最终的http回复。
这一模板系统可以与Django的其它功能相互合作。上一回,我们从数据库中提取出了数据。如果将数据库中的数据放入到context中,那么就可以将数据库中的数据传送到模板。
修改上次的west/views.py中的staff:
1
2
3
4
5
|
def
staff
(
request
)
:
staff_list
=
Character
.
objects
.
all
(
)
staff_str
=
map
(
str
,
staff_list
)
context
=
{
'label'
:
' '
.
join
(
staff_str
)
}
return
render
(
request
,
'templay.html'
,
context
)
|
练习: 显示上面的staff页面。
“勇士翻过高山,并非因为他不害怕。”
Django实际上提供了丰富的模板语言,可以在模板内部有限度的编程,从而更方便的编写视图和传送数据。
我们下面体验一下最常见的循环与选择。
上面的staff中的数据实际上是一个数据容器,有三个元素。刚才我们将三个元素连接成一个字符串传送。
实际上,利用模板语言,我们可以直接传送数据容器本身,再循环显示。修改staff()为:
1
2
3
|
def
staff
(
request
)
:
staff_list
=
Character
.
objects
.
all
(
)
return
render
(
request
,
'templay.html'
,
{
'staffs'
:
staff_list
}
)
|
从数据库中查询到的三个对象都在staff_list中。我们直接将staff_list传送给模板。
将模板templay.html修改为:
1
2
3
|
{
%
for
item
in
staffs
%
}
p
>
{
{
item
.
id
}
}
,
{
{
item
}
}
p
>
{
%
endfor
%
}
|
我们以类似于Python中for循环的方式来定义模板中的for,以显示staffs中的每个元素。
还可以看到,对象.属性名的引用方式可以直接用于模板中。
选择结构也与Python类似。根据传送来的数据是否为True,Django选择是否显示。使用方式如下:
1
2
3
4
5
6
7
|
{
%
if
condition1
%
}
.
.
.
display
1
{
%
elif
condiiton2
%
}
.
.
.
display
2
{
%
else
%
}
.
.
.
display
3
{
%
endif
%
}
|
其中的elif和else和Python中一样,是可以省略的。
“勇士屠杀恶龙,并非因为他不恐惧。”
模板可以用继承的方式来实现复用。我们下面用templay.html来继承base.html。这样,我们可以使用base.html的主体,只替换掉特定的部分。
新建templates/west/base.html:
1
2
3
4
5
6
7
8
9
10
11
12
|
<
html
>
head
>
title
>
templaytitle
>
<
head
>
<
body
>
h1
>
come
from
base
.
htmlh1
>
{
%
block
mainbody
%
}
p
>
originalp
>
{
%
endblock
%
}
<
body
>
<
html
>
|
该页面中,名为mainbody的block标签是可以被继承者们替换掉的部分。
我们在下面的templay.html中继承base.html,并替换特定block:
1
2
3
4
5
6
7
8
9
|
{
%
extends
"base.html"
%
}
{
%
block
mainbody
%
}
{
%
for
item
in
staffs
%
}
p
>
{
{
item
.
id
}
}
,
{
{
item
.
name
}
}
p
>
{
%
endfor
%
}
{
%
endblock
%
}
|
第一句说明templay.html继承自base.html。可以看到,这里相同名字的block标签用以替换base.html的相应block。
“勇士穿过地狱火焰,因为,她值得。”
使用模板实现视图分离。
数据传递,模板变量,模板循环与选择,模板继承。