`标签里,放到 body_block 区块下面。这 样所有继承的子模板都将具有这些链接。这组链接前面有条水平线(`
`),目的是从 视觉上把链接与 body_block 区块分开。
# 8.3 模板继承
创建好基模板之后,接下来要更新其他模板,让它们继承基模板。先重构 rango/category.html 模 板。
首先把模板中重复的 HTML 代码删除,只留下对所在页面有用的模板标签或命令。然后在模板的 开头添加下面这行代码:
```
{% extends 'rango/base.html' %}
```
extends 命令接受一个参数,即要扩展(继承)的模板。这个参数的值是个路径,相对项目的 templates 目录。例如,Rango 应用中的模板都继承 rango/base.html,而不是 base.html。继续修改 category.html 模板,如下所示:
```
{% extends 'rango/base.html' %}
{% load staticfiles %}
{% block title_block %}
{{ category.name }}
{% endblock %}
{% block body_block %}
{% if category %}
{{ category.name }}
{% if pages %}
{% else %}
No pages currently in category.
{% endif %}
Add a Page
{% else %}
The specified category does not exist!
{% endif %}
{% endblock %}
```
>◆加载 staticfiles◆
用到静态文件的每个模板都要在文件顶部添加 {% load staticfiles %}。否则会导致错误! 对 Django 模板而言,必须在用到模块的每个模板中导入模块。这与面向对象编程语言(例 如 Java)有所不同。在面向对象编程语言中,导入的模块会沿着类继承体系向下延伸。注意 我们是如何使用 url 模板标签引用` rango/
/add_page/ `URL 模式的。 category.slug 作为参数传给 url 模板标签,Django 模板引擎会据此生成正确的 URL。
现在 category.html 模板继承自 rango/base.html,并扩展了 title_block 和 body_block 区块,变得 简洁多了。category.html 模板不必是完整的 HTML 文档,因为 base.html 提供了所需的结构。我们 只需在基模板的基础上添加所需的内容,这样便能渲染得到完整的 HTML 文档,发给客户端。渲 染得到的 HTML 文档符合标准,包含所需的各部分标记,例如第一行的文档类型声明。
# 8.4 render() 函数和 request 上下文
视图有多种渲染模板的方式,不过首选的是 Django 提供的简洁方式,即 render() 函数。 render() 函数的第一个参数是 request 上下文,里面有大量信息,包括会话和用户等等,详情参 见 Django 文档。把 request 传给模板意味着,我们可以在模板中访问其中的信息。下一章将访问 关于用户的信息。现在请检查一下所有视图,确保都是用 render() 函数渲染模板的。如若不然, 后面就得不到所需的信息。
如何检查?
下面以 about() 视图为例说明如何检查和修改。about() 视图目前的实现方式是硬编码响应 字符串,如下所示。注意,我们只是发送字符串,没有用到传入视图的 request 参数。
```
def about(request):
return HttpResponse('Rango says: Here is the about page. Index')
```
为了改用模板,我们要调用 render() 函数,并传入 request 对象。这样修改之后,模板引擎 就能访问关于请求(例如请求类型,GET 或 POST)和用户(例如用户的状态,参见第 9 章)的信息了。
```
def about(request):
# 打印请求方法,是 GET 还是 POST
print(request.method)
# 打印用户名,如未登录,打印“AnonymousUser”
print(request.user)
return render(request,'rango/about.html',{})
```
注意,render() 函数的最后一个参数是上下文字典,用于把额外的数据传给 Django 模板引 擎。因为这里没什么额外数据要传给模板,所以使用一个空字典。
# 8.5 自定义模板标签
如果能在每个页面的侧边栏中展示有哪些分类可浏览就好了。根据目前所学,这个想法可以这样 实现:
❏ 在 base.html 模板中添加一些代码,显示分类列表
❏ 在每个视图中通过 Category 对象获取所有分类,通过上下文字典传给模板
可是这样的实现方式非常不好,因为我们要在所有视图中重复添加相同的代码。为了不违背 DRY 原则,我们可以自定义模板标签,把相关的操作封装起来。
### 定义模板标签
新建 rango/templatetags 目录,然后在其中新建两个模块:一个命名为 __init__.py,内容为空;另 一个命名为 rango_template_tags.py,写入下述代码。
```
from django import template
from rango.models import Category
register = template.Library()
@register.inclusion_tag('rango/cats.html')
def get_category_list():
return {'cat': Category.objects.all()}
```
这段代码定义了一个名为 get_category_list() 的函数,返回结果为分类列表。但是从 register.inclusion_tag() 装饰器可以看出,这个函数需要 rango/cats.html 模板的支持。创建这个 模板,写入下述 HTML 标记。
```
{% if cats %}
{% for c in cats %}
- {{ c.name }}
{% endfor %}
{% else %}
- There are no categories present.
{% endif %}
```
为了在 base.html 模板中使用这个模板标签,首先要在文件顶部添加 {% load rango_template_tags %}。然后创建一个区块,表示侧边栏。在侧边栏中通过下述代码调用我们自定义的模板标签。
```
{% block sidebar_block %}
{% get_category_list %}
{% endblock %}
```
试试看。现在继承自 base.html 模板的每个页面都将显示分类列表(稍后移到侧边)。
### 参数化模板标签
为了提高灵活性,可以参数化模板标签。举个例子:突出显示当前查看的分类。添加参数很简 单,参照下述代码修改 get_category_list() 函数。
```
def get_category_list(cat=None):
return {'cats': Category.objects.all(), 'act_cat': cat}
```
注意,cat 参数是可选的,如果未传入,默认为 None。 接下来修改 base.html 模板,传入当前查看的分类(仅当存在时)。
```
{% block sidebar_block %}
{% get_category_list category %}
{% endblock %}
```
此外,还要修改 cats.html。
```
{% if cats %}
{% for c in cats %}
{% if c == act_cat %}
-
{{ c.name }}
{% else %}
-
{{ c.name }}
{% endif %}
{% endfor %}
{% else %}
- There are no categories present.
{% endif %}
```
这里我们检查显示的分类与 for 循环当前遍历的分类是否相同(c == act_cat),如果相同,通 过 标签加粗,突出显示当前查看的分类名。
# 8.6 小结
本章讨论了下述内容:
❏ 使用 url 模板标签避免硬编码 URL
❏ 借助模板继承减少样板代码量
❏ 通过自定义模板标签减少视图中重复的代码
经过这些改进之后,模板代码比以前更简洁,也更易维护了。当然,Django 模板的功能还有很 多,详情参阅 Django 文档。