Django 模板(template)

添加模板文件

继续使用之前的 dj4 项目,我们在 learn 这个 app 中添加功能。开始编辑其 views.py 文件,增加一个 home 方法:

1
2
3
4
5
6
7
8
9
$ vim learn/views.py
from django.shortcuts import render
from django.http import HttpResponse

def index(request):
return HttpResponse(u'Hello Shiyanlou!')

def home(request):
return render(request, 'home.html')

然后我们在 learn 目录下增加一个 templates 目录,用来放置模板文件。在 Django 的默认配置中,模板文件放在对应 app 的 templates 的目录中。所以我们新建的这个目录中存放的模板文件,Django 会自动在此查询文件。

新建 templates 目录,创建 home.html 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cd learn
$ mkdir templates && cd templates
$ vim home.html

<!DOCTYPE html>
<html>
<head>
<title>shiyanlou</title>
</head>
<body>
Programming every time, everywhere.
</body>
</html>

老规矩,在修改完 views.py 之后,来配置一下链接路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ vim dj4/urls.py
from django.conf.urls import include, url
from django.contrib import admin
from learn import views as learn_views
from calc import views as calc_views

urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
# url(r'$', learn_views.index),
# url(r'^add/', calc_views.add, name = 'add'),
# url(r'^add/(\d+)/(\d+)/$', calc_views.add2, name = 'add2'),
url(r'^$', learn_views.home, name = 'home'),
]

接下来测试查看效果:

发现我们的 html 模板文件成功加载了。这样相对于只有输出日志的文字流灵活更多了。但是,Django 的模板文件不仅仅停留在 html 加载,而是一种新的标记语言。

深入模板学习

在很多时候,我们所做的网站有很多通用的部分。例如导航条、页底、访问统计等等。我们可以使用 include 关键字,来通过引入通用文件来插入 html 标记中:

例如有这么一个 base.html 模板文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
<title>shiyanlou</title>
</head>
<body>

{% include 'nav.html' %}

{% block content %}
<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% endblock %}

{% include 'bottom.html' %}

{% include 'tongji.html' %}

</body>
</html>

这就好比每一个页面的基础模板,根据每个网页的不同,再进行定制的修改此文件即可。而定制部分即上例中的 block 关键字部分。

Django 模板查找机制:查找模板的过程是在每个 app 的 templates 目录中找,每个 app 的 template 形成一个目录列表,Django 遍历这个列表,一个个目录进行查询,当在某一个目录中找到时就停止,所有的都遍历之后,仍旧找不到指定的模板,则进入 Template Not Found 状态。这一点与 Python 对包的查询过程十分类似。这样的设计当然也有利弊,好处是一个 app 可以用另一个 app 的模板文件,而弊端就是可能会发现查询错误的情况,所以我们在 templates 中再以 app 名对其进行细分,则会更加清晰。例如下面这个项目的目录结构,就是笔者比较推荐的:

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作为名称的一部分,就不会混淆。

模板的使用进阶

条件判断与循环

修改 learn 的 views.py 文件的 home 方法:

1
2
3
def home(request):
string = u"Shiyanlou is very good!"
return render(request, 'home.html', {'string': string})

然后我们在 home.html 中,使用 标记就可以把改字符串传递进来:

当然,不仅可以传递字符串,还可以将 List 内容传递至模板中。但是有 List 的地方我们只有遍历才能将其数据全部取出。所以模板文件可以使用 for 循环对传入的 List 变量进行遍历操作:

1
2
3
def home(request):
TutorialList = ["HTML", "CSS", "jQuery", "Python", "Django"]
return render(request, 'home.html', {'TutorialList': TutorialList})

在视图文件中,我们使用下列标记来进行遍历:

1
2
3
{% for i in TutorialList %}
{{ i }},
{% endfor %}

我们将课程数据传入页面并展示,但是发现还有一些小的瑕疵,即在字符串 Django 后面仍旧有一个逗号。解决的方法很多,但是这里为了让你了解模板,我们继续使用模板中的条件语句标记来修改这个瑕疵

条件判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ vim learn/templates/home.html
<!DOCTYPE html>
<html>
<title>shiyanlou</title>
<body>
Programming every time, everywhere. There are some courses: <br />
{% for i in TutorialList %}
{{ i }}
{% if not forloop.last %}
,
{% endif %}
{% endfor %}

</body>
</html>

这里我们只需要判断是否为 List 中的最后一个元素即可。于是我们解决了这个 bug。

这里还有一些用法,请读者们自行尝试,我在这里为大家列出。

变量 描述
forloop.counter 索引从 1 开始算
forloop.counter0 索引从 0 开始算
forloop.revcounter 索引从最大长度到 1
forloop.revcounter0 索引从最大长度到 0
forloop.first 当遍历的元素为第一项时为真
forloop.last 当遍历的元素为最后一项时为真
forloop.parentloop 用在嵌套的 for 循环中,获取上一层 for 循环的 forloop

当然,Django 的模板功能远远不止这些,如果想深入了解,请大家阅读官方文档。