模板高级用法
模板子目录
并不是所有的模板文件都一定放在templates这个模板标签中,我们可以使用文件夹的嵌套让模板文件的分类更加明确,在选择模板的时候也可以直接指明模板的路径。只需在调用 get_template() 时,把子目录名和一条斜杠添加到模板名称之前,如:
t = get_template('dateapp/current_datetime.html')
由于render_to_response() 只是对get_template() 的简单封装,你可以对 render_to_response() 的第一个参数做相同处理。
return render_to_response('dateapp/current_datetime.html', {'current_date':now})
对子目录树的深度没有限制,你想要多少层都可以。只要你喜欢,用多少层的子目录都无所谓。
include标签
内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {%include %} 来减少重复。
下面这两个例子都包含了 nav.html 模板。这两个例子是等价的,它们证明单/双引号都是允许的。
{% include 'nav.html' %}{% include"nav.html" %}
下面的例子包含了 includes/nav.html 模板的内容:
{% include 'includes/nav.html' %}
下面的例子包含了以变量 template_name 的值为名称的模板内容:
{% include template_name %}
和在 get_template() 中一样,对模板的文件名进行判断时会在所调取的模板名称之前加上来自 TEMPLATE_DIRS 的模板目录。
所包含的模板执行时的 context 和包含它们的模板是一样的。举例说,考虑下面两个模板文件:
# mypage.html
<html><body>{% include"includes/nav.html" %}<h1>{{ title}}</h1></body></html>
# includes/nav.html
<div id="nav"> You are in: {{ current_section }}</div>
如果你用一个包含 current_section的上下文去渲染 mypage.html这个模板文件,这个变量将存在于它所包含(include)的模板里。
如果{% include %}标签指定的模板没找到,Django将会在下面两个处理方法中选择一个:
如果 DEBUG 设置为 True ,你将会在 Django 错误信息页面看到 TemplateDoesNotExist 异常。
如果 DEBUG 设置为 False ,该标签不会引发错误信息,在标签位置不显示任何东西。
模板继承
你用 Django 模板系统来创建整个 HTML 页面。这就带来一个常见的 Web 开发问题:在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?Django 通过刚才讲述的 {% include %} 解决了部分问题。但是用Django 解决此类问题的首选方法是使用更加优雅的策略——模板继承。本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
通过实例来体会一下这种做法:
<!DOCTYPE>
<html lang="en">
<head>
<title>The current time</title>
</head>
<body>
<h1>My helpful timestampsite</h1>
<p>It is now {{ current_date}}.</p>
<hr>
<p>Thanks for visiting mysite.</p>
</body>
</html>
<!DOCTYPE>
<html lang="en">
<head>
<title>Future time</title>
</head>
<body>
<h1>My helpful timestampsite</h1>
<p>In {{ hour_offset }} hour(s), itwill be {{ next_time }}.</p>
<hr>
<p>Thanks for visiting mysite.</p>
</body>
</html>
很明显,我们刚才重复了大量的 HTML 代码。想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。也许你会把模板头部的一些代码保存为 header.html 文件:
<!DOCTYPE HTML PUBLIC "‐//W3C//DTDHTML 4.01//EN">
<html lang="en">
<head>
你可能会把底部保存到文件 footer.html :
<hr>
<p>Thanks for visiting mysite.</p>
</body>
</html>
对基于 include 的策略,头部和底部的包含很简单。麻烦的是中间部分。在此范例中,每个页面都有一个<h1>My helpful timestamp site</h1> 标题,但是这个标题不能放在 header.html 中,因为每个页面的<title>是不同的。 如果我们将 <h1> 包含在头部,我们就不得不包含 <title> ,但这样又不允许在每个页面对它进行定制。 何去何从呢?
Django 的模板继承系统解决了这些问题。 你可以将其视为服务器端include 的逆向思维版本。你可以对那些不同的代码段进行定义,而不是共同代码段。
第一步是定义基础模板,该框架之后将由子模板所继承。以下是我们目前所讲述范例的基础模板:
<!DOCTYPE>
<html lang="en">
<head>
<title>{% block title %}{% endblock%}</title>
</head>
<body>
<h1>My helpful timestampsite</h1>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>Thanks for visiting mysite.</p>
{% endblock %}
</body>
</html>
这个叫做 base.html 的模板定义了一个简单的HTML 框架文档。子模板的作用就是重载、添加或保留那些块的内容。我们使用一个以前已经见过的模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个 {% block%} 标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
现在我们已经有了一个基本模板,我们可以修改 current_datetime.html 模板来 使用它:
{% extends "base.html" %}
{% block title %}The current time{%endblock %}
{% block content %}
<p>It is now {{ current_date}}.</p>
{% endblock %}
再为 hours_ahead 视图创建一个模板,看起来是这样的:
{% extends "base.html" %}
{% block title %}Future time{% endblock %}
{% block content %}
<p>In {{ hour_offset }} hour(s), itwill be {{ next_time }}.</p>
{% endblock %}
每个模板只包含对自己而言独一无二的代码。无需多余的部分。如果想进行站点级的设计修改,仅需修改 base.html ,所有其它模板会立即反映出所作修改。以下是其工作方式。 在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该
模板是一个子模板。模板引擎立即装载其父模板,即本例中的base.html。此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由 {% block title %} 替换,同样地,网页的内容一块将由 {% block content %} 替换。注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。你可以根据需要使用任意多的继承次数。
使用继承的一种常见方式是下面的三层法:
1. 创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
2. 为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对 base.html 进行拓展,并包含区域特定的风格与设计。
3. 为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。
这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。
以下是使用模板继承的一些诀窍:
如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。
如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
如果你需要访问父模板中的块的内容,使用 {{ block.super }} 这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在 父 模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
{% extends %} 对所传入模板名称使用的加载方法和 get_template() 相同。 也就是说,会将模板名称被添加到 TEMPLATE_DIRS 设置之后。
多数情况下, {% extends %} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。这使得你能够实现一些很酷的动态功能。
全局量的传入
我们直到模板的变量由views.py 传入,但是如果有些变量在几乎每个模板中都会使用,那么在views.py文件中的每一个方法里都要组织对应的数据用于页面解析,那绝对是十分麻烦的事情,这个时候我们就需要把这些数据保存成全局变量的形式让他可以轻松渲染到每一个页面。
之前我们说到settings.py文件里面有模板配置,其中有个OPTIONS。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
注意到 OPTIONS.context_processors 里面有4条信息,其实每一条信息都对应一个函数,这里的原理是设置每一个函数的返回值作为Template的全局变量,拿最简单的函数request举例:
from django.template importcontext_processors.py
def request(request):
return {'request': request}
它只是简单地返回一个字典 {‘request’: request} 这就不难理解为什么在Django的模板系统里面,所有的Template我们都可以直接访问request.user来获取对应的用户了。由于request被设置成全局变量,以字典的形式传到后台去了。
由此我们也可以编写个类似的函数,你可以单独写一个文件,或者直接写在views文件中也可以:
def global_setting(request):
SITE_URL = settings.SITE_URL
SITE_NAME = settings.SITE_NAME
SITE_DESC = settings.SITE_DESC
return locals()
在settings.py文件里面设置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# 假设在blog app的views文件中,则加上这句
'blog.views.global_ settting'
],
},
},
]
就OK 了,这样就可以在templates里面引用所写的函数中的变量了