在之前的章节中,你可能觉得例子中视图返回文本有点不妥。即是, HTML 是直接写在 Python 代码中的。

 

 

 

这种做法会导致这些问题:

 

 

 

  • 要做任何设计上的更改就必须改写 Python 代码。网站的设计风格的更变一般来说会比更改后台的 Ptyhon 代码来得频繁,因此如果能够更改设计而不用更改 Python 变得尤为方便。2  
  • Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。设计人员和 HTML/CSS 编写人员都不应该通过编辑 Python 代码来完成自己的工作;他们应该处理的是 HTML。3
  • 同理,程序员编写 Python 代码和设计人员制作模板同时进行的工作方式效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python 又包含 HTML 的文件的编辑工作。 

基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。我们可以使用Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。

 

 

 

模板系统基本知识

 

模板系统基本知识

 

让我们深入分析一个简单的例子模板。该模板描述了一个向某个与公司签单人员致谢 HTML 页面。可将其视为一个格式信函:

5

 

 

<html>
<head><title>Ordering notice</title></head>

<body>

<p>Dear {{ person_name }},</p>

<p>Thanks for placing an order from {{ company }}. It's scheduled to
ship on {{ ship_date|date:"F j, Y" }}.</p>

<p>Here are the items you've ordered:</p>

<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>

{% if ordered_warranty %}
<p>Your warranty information will be included in the packaging.</p>
{% endif %}

<p>Sincerely,<br />{{ company }}</p>

</body>
</html>

4

该模板是一段添加了些许变量和模板标签的基础 HTML 。让我们逐句过一遍:

 

 

 

用两个大括号括起来的文字(例如{{person_name}} 变量(variable) 。这意味着将按照给定的名字插入变量的值。如何指定变量的值呢?稍后就会说明。

 

 

 

被大括号和百分号包围的文本(例如{%ifordered_warranty%} 模板标签(template tag) 。标签(tag)定义比较明确,即:仅通知模板系统完成某些工作。

3

 

 

这个示例模板包含两个标签(tag):{%foriteminitem_list%}for{%ifordered_warranty%}if 标签)。

4

 

 

forifordered_warrantyTrue{%ifordered_warranty%}{%endif%}{%else%} 以及其他多种逻辑判断方式。

1

 

 

最后,这个模板的第二段落有一个 filter{{ship_date|date:"Fj,Y"}}ship_datedate"Fj,Y"date| )来调用的,就和Unix管道一样.

1

Django 模板含有很多内置的tags和filters,我们将陆续进行学习. 附录F列出了很多的tags和filters的列表,熟悉这些列表对你来说是个好建议. 学习完第十章,你就明白怎么去创建自己的filters和tags了.

2

如何使用模板系统

 

想要在Python代码中使用模板系统,只需遵循下面两个步骤:

 

 

 

1.可以用原始的模板代码字符串创建一个TemplateTemplate 对象;

 

 

 

  1. 调用Templaterender() 方法并提供给他变量(i.e., 内容).它将返回一个完整的模板字符串内容,包含了所有标签块与变量解析后的内容.1

以下部分逐步的详细介绍

 

创建模板对象

 

创建一个TemplateTemplatedjango.template 模块中,构造函数接受一个参数,原始模板代码。让我们深入挖掘一下 Python的解释器看看它是怎么工作的。

 

 

 

关于交互式解释器的例子

1

 

 

在本书中,我们喜欢用和Python解释器的交互来举例。你可以通过三个> (>>> ) 识别它们,它们相当于Python解释器的提示符。如果你要拷贝例子,请不要拷贝这3个>字符。

 

 

 

多行语句则在前面加了3个小数点(... ),例如:

 

>>> print """This is a
... string that spans
... three lines."""
This is a
string that spans
three lines.
>>> def my_function(value):
...     print value
>>> my_function('hello')
hello

 

这3个点是Python解释器自动加入的,不需要你的输入。我们包含它们是为了忠实呈现解释器的真实输出。同样道理,拷贝时不要拷贝这3个小数点符号。

 

 

 

转到project目录(在第二章由django-admin.pystartprojectpythonmanage.pyshell 启动交互界面。下面是一些基本操作:

 

>>> from django.template import Template
>>> t = Template("My name is {{ name }}.")
>>> print t

 

如果你跟我们一起做,你将会看到下面的内容:

2

 

 

<django.template.Template object at 0xb7d5f24c>

 

0xb7d5f24cTemplate 对象的ID。

 

 

 

Django 设置

 

 

 

当你使用Django时,你需要告诉Django使用哪个配置。在交互模式下,通常运行命令pythonmanage.pyshell 来做这个,附录E里还有一些其他的一些选项。

 

 

 

当你创建一个TemplateTemplate() 时就会拋出``TemplateSyntaxError`` 异常:

 

>>> from django.template import Template
>>> t = Template('{% notatag %} ')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  ...
  django.template.TemplateSyntaxError: Invalid block tag: 'notatag'

 

系统会在下面的情形抛出TemplateSyntaxError 异常:

 

 

 

  • 无效的块标签 
  • 无效的参数 
  • 无效的过滤器 
  • 过滤器的参数无效 
  • 无效的模板语法 
  • 未封闭的块标签 (针对需要封闭的块标签) 

模板渲染

 

一旦你创建一个Template context 来传递数据给它。一个context是一系列变量和它们值的集合。模板使用它来赋值模板变量标签和执行块标签。

 

 

 

context在Django里表现为Contextdjango.templateTemplaterender() 方法并传递context来填充模板:

 

 

 

>>> from django.template import Context, Template
>>> t = Template("My name is {{ name }}.")
>>> c = Context({"name": "Stephane"})
>>> t.render(c)
'My name is Stephane.'

2

字典和Contexts

 

 

 

Python的字典数据类型就是关键字和它们值的一个映射。ContextContext 还提供更多的功能,请看第十章。

 

 

 

mi72dV <a href=”http://slilwpafggxi.com/“>slilwpafggxi</a>, [url=http://craygkxgfnjo.com/]craygkxgfnjo[/url], [link=http://xpysyedxmcxf.com/]xpysyedxmcxf[/link],http://wiehcqcfyait.com/

 

 

 

eS167R <a href=”http://nuwbmtossawf.com/“>nuwbmtossawf</a>, [url=http://pevmvdxtirqp.com/]pevmvdxtirqp[/url], [link=http://qwjhswpigqhe.com/]qwjhswpigqhe[/link],http://icplrpsmsykr.com/

 

 

 

>>> from django.template import Template, Context
>>> raw_template = """<p>Dear {{ person_name }},</p>
...
... <p>Thanks for ordering {{ product }} from {{ company }}. It's scheduled
... to ship on {{ ship_date|date:"F j, Y" }}.</p>
...
... {% if ordered_warranty %}
... <p>Your warranty information will be included in the packaging.</p>
... {% endif %}
...
... <p>Sincerely,<br />{{ company }}</p>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'John Smith',
...     'product': 'Super Lawn Mower',
...     'company': 'Outdoor Equipment',
...     'ship_date': datetime.date(2009, 4, 2),
...     'ordered_warranty': True})
>>> t.render(c)
"<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from
Outdoor Equipment. It's scheduled \nto ship on April 2, 2009.</p>\n\n\n
<p>Your warranty information will be included in the packaging.</p>\n\n\n
<p>Sincerely,<br />Outdoor Equipment</p>"

2

QkcxNA <a href=”http://ghswlopuxpss.com/“>ghswlopuxpss</a>, [url=http://uesbhjxqyrme.com/]uesbhjxqyrme[/url], [link=http://amccuttzklrf.com/]amccuttzklrf[/link],http://dpbpfehtyyrp.com/

 

 

 

6VM22m <a href=”http://dbmowofwmqtj.com/“>dbmowofwmqtj</a>, [url=http://yghczevyzoxi.com/]yghczevyzoxi[/url], [link=http://dzgwelqaaumn.com/]dzgwelqaaumn[/link],http://gzeagucqbifm.com/

 

 

 

我们把模板原始文本保存到变量raw_template 。注意到我们使用了三个引号来 标识这些文本,因为这样可以包含多行。这是Python的一个语法。

 

B3il2y <a href=”http://mumjpopnrkgr.com/“>mumjpopnrkgr</a>, [url=http://ogxuziylkwph.com/]ogxuziylkwph[/url], [link=http://ujomajdwhpdw.com/]ujomajdwhpdw[/link],http://eeewvcpxqgiz.com/

 

 

 

oOyjcc <a href=”http://lnvugwgkrqns.com/“>lnvugwgkrqns</a>, [url=http://bwjhvzeujnbf.com/]bwjhvzeujnbf[/url], [link=http://jlkuadprtrmg.com/]jlkuadprtrmg[/link],http://lwsaislcqgyk.com/

 

 

 

xF3o1U <a href=”http://jtrhmwpjydue.com/“>jtrhmwpjydue</a>, [url=http://tqujmqryiqyh.com/]tqujmqryiqyh[/url], [link=http://krynzdpnuxmr.com/]krynzdpnuxmr[/link],http://acqdbsycufny.com/

 

 

 

OCJRzD <a href=”http://lxvstsvmnkeu.com/“>lxvstsvmnkeu</a>, [url=http://fpyfpezybddz.com/]fpyfpezybddz[/url], [link=http://vgsijpejdnlj.com/]vgsijpejdnlj[/link],http://zjecpbzvoeub.com/

 

 

 

XXfdx0 <a href=”http://lbrwkxsboiyb.com/“>lbrwkxsboiyb</a>, [url=http://mjiqpepliiop.com/]mjiqpepliiop[/url], [link=http://qtujzswofleg.com/]qtujzswofleg[/link],http://nmyfaostpqbk.com/

3

 

 

8FyRLj <a href=”http://txbmfvgmfnoh.com/“>txbmfvgmfnoh</a>, [url=http://ojfssfhusbje.com/]ojfssfhusbje[/url], [link=http://ptooojtphvzm.com/]ptooojtphvzm[/link],http://sgyftkqcljls.com/

 

 

 

29oF4V <a href=”http://zotapxvjcacj.com/“>zotapxvjcacj</a>, [url=http://apzuywrdbnja.com/]apzuywrdbnja[/url], [link=http://hieczjpoybhd.com/]hieczjpoybhd[/link],http://rcrdzjslaocp.com/

 

同一模板,多个上下文

 

一旦有了模板 对象,你就可以通过它渲染多个背景(context),例如:

 

 

 

>>> from django.template import Template, Context
>>> t = Template('Hello, {{ name }}')
>>> print t.render(Context({'name': 'John'}))
Hello, John
>>> print t.render(Context({'name': 'Julie'}))
Hello, Julie
>>> print t.render(Context({'name': 'Pat'}))
Hello, Pat

1

无论何时像这样使用同一模板源渲染多个背景,只创建 一次模板render() 将会更加高效。

 

 

 

# Bad
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))

# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))

 

3F2aSP <a href=”http://yrsdcgmsmmvq.com/“>yrsdcgmsmmvq</a>, [url=http://ushwgbptbdcs.com/]ushwgbptbdcs[/url], [link=http://qegbutcfxpyt.com/]qegbutcfxpyt[/link],http://smuqjabafbux.com/

 

 

 

vZ2M65 <a href=”http://anpzyranmadt.com/“>anpzyranmadt</a>, [url=http://gmhltltkepox.com/]gmhltltkepox[/url], [link=http://eezbqoibxtnh.com/]eezbqoibxtnh[/link],http://jxuwsvnayvjd.com/

 

 

 

在到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,还有一个datetime.date 范例。然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。

 

 

 

BRAiMC <a href=”http://ikhprolqdygn.com/“>ikhprolqdygn</a>, [url=http://rrlvuagydxyw.com/]rrlvuagydxyw[/url], [link=http://untixofwstwp.com/]untixofwstwp[/link],http://wcaoxicoikju.com/

 

 

 

bU6gvx <a href=”http://fwxlgcanljlx.com/“>fwxlgcanljlx</a>, [url=http://egmnwvgfhckm.com/]egmnwvgfhckm[/url], [link=http://lkcahhkeerag.com/]lkcahhkeerag[/link],http://dsdvrtizevvb.com/

 

 

 

>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

1

同样,也可以通过句点来访问对象的属性。比方说, Python 的datetime.dateyearmonthday 几个属性,你同样可以在模板中使用句点来访问这些属性:

 

 

 

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

 

zDA3EL <a href=”http://iaszhgfjhprs.com/“>iaszhgfjhprs</a>, [url=http://varaookhpiko.com/]varaookhpiko[/url], [link=http://ylafmlnqfkzu.com/]ylafmlnqfkzu[/link],http://wgnkbwbjjrnc.com/

 

 

 

>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

4

aAJSep <a href=”http://zvhegtcaluza.com/“>zvhegtcaluza</a>, [url=http://tdvffhmfxotd.com/]tdvffhmfxotd[/url], [link=http://fvdzdwdtqpgm.com/]fvdzdwdtqpgm[/link],http://sqjughqweakv.com/

 

 

 

>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

 

注意你不能在方法调用中使用圆括号。而且也无法给该方法传递参数;你只能调用不需参数的方法。(我们将在本章稍后部分解释该设计观。)

 

 

 

最后,句点也可用于访问列表索引,例如:

 

 

 

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

 

y4ZrzJ <a href=”http://icxpmxlwrpsh.com/“>icxpmxlwrpsh</a>, [url=http://ixhqmfzpzaka.com/]ixhqmfzpzaka[/url], [link=http://syyzcospavtz.com/]syyzcospavtz[/link],http://tbvcpgfogmdp.com/

1

 

 

Python 列表类型

 

 

 

hi0RIH <a href=”http://worsflxrfrnm.com/“>worsflxrfrnm</a>, [url=http://zfknengqpebj.com/]zfknengqpebj[/url], [link=http://cmjbednwvygj.com/]cmjbednwvygj[/link],http://dwhryqholppa.com/

 

 

 

句点查找规则可概括为:当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:

 

 

 

FK4UOL <a href=”http://tpjhvrppgruu.com/“>tpjhvrppgruu</a>, [url=http://wqtlkckntxnr.com/]wqtlkckntxnr[/url], [link=http://rgqqkjlxzpez.com/]rgqqkjlxzpez[/link],http://pncpsommbvpg.com/

 

 

 

  • 属性查找 (比如foo.bar ) 

zRCBoI <a href=”http://meqsrkbxbfdl.com/“>meqsrkbxbfdl</a>, [url=http://hmpygbvtfqig.com/]hmpygbvtfqig[/url], [link=http://kyfvyyyzewdc.com/]kyfvyyyzewdc[/link],http://ljmpizgopmat.com/

1

 

 

URPaJ1 <a href=”http://swoyoudsnvjh.com/“>swoyoudsnvjh</a>, [url=http://kmbeppskhdgn.com/]kmbeppskhdgn[/url], [link=http://aisoffmmdeqx.com/]aisoffmmdeqx[/link],http://jwvrkxhhpoci.com/

1

 

 

mor7zk <a href=”http://mcxvcbvzubvy.com/“>mcxvcbvzubvy</a>, [url=http://xkuyifojsdfc.com/]xkuyifojsdfc[/url], [link=http://ibiowlkhuuht.com/]ibiowlkhuuht[/link],http://hiwwlgdvapqf.com/

 

 

 

GHPzQZ <a href=”http://qgwxsorffznt.com/“>qgwxsorffznt</a>, [url=http://ukkibofoynaz.com/]ukkibofoynaz[/url], [link=http://hopzysestjfr.com/]hopzysestjfr[/link],http://pbnkwphfoqsn.com/

 

 

 

>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name.upper }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'SALLY is 43 years old.'

 

Kq4heW <a href=”http://ceiplttkuvdd.com/“>ceiplttkuvdd</a>, [url=http://orylxajdtysr.com/]orylxajdtysr[/url], [link=http://aioltuxkaqdv.com/]aioltuxkaqdv[/link],http://zyijpbxgojop.com/

 

 

 

STg2dV <a href=”http://dzptmkaihfem.com/“>dzptmkaihfem</a>, [url=http://shqeknyjncnb.com/]shqeknyjncnb[/url], [link=http://unnqozabqngj.com/]unnqozabqngj[/link],http://akjojnqkvtto.com/

 

 

 

6DorJX <a href=”http://fphetowfxofh.com/“>fphetowfxofh</a>, [url=http://xxbndkowrdvs.com/]xxbndkowrdvs[/url], [link=http://ztitjjxwxyre.com/]ztitjjxwxyre[/link],http://rwasxseakbmt.com/

1

 

 

>>> t = Template("My name is {{ person.first_name }}.")
>>> class PersonClass3:
...     def first_name(self):
...         raise AssertionError, "foo"
>>> p = PersonClass3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo

>>> class SilentAssertionError(AssertionError):
...     silent_variable_failure = True
>>> class PersonClass4:
...     def first_name(self):
...         raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
"My name is ."

1

55GDeO <a href=”http://rxddbqlbnfcp.com/“>rxddbqlbnfcp</a>, [url=http://dbtfqgzzmzqn.com/]dbtfqgzzmzqn[/url], [link=http://nktzhdypapql.com/]nktzhdypapql[/link],http://fxwtetcucbyi.com/

 

 

 

Nq0kOm <a href=”http://jpxwidvdstmb.com/“>jpxwidvdstmb</a>, [url=http://mkpleljfurxz.com/]mkpleljfurxz[/url], [link=http://pfnwrmlgxdpu.com/]pfnwrmlgxdpu[/link],http://henvcjppfima.com/

 

 

 

例如,你的一个BankAccountdelete(){{account.delete}} 这样的方法调用。

 

kfagrl <a href=”http://dcvtlyxapibb.com/“>dcvtlyxapibb</a>, [url=http://yqmovjajuigf.com/]yqmovjajuigf[/url], [link=http://lemsdmygyegg.com/]lemsdmygyegg[/link],http://rlottjbwdbwz.com/

 

 

 

def delete(self):
    # Delete the account
delete.alters_data = True

 

frcBdl <a href=”http://jdkchhabnzpm.com/“>jdkchhabnzpm</a>, [url=http://urxocwbeekzu.com/]urxocwbeekzu[/url], [link=http://bbkzrkqzxveg.com/]bbkzrkqzxveg[/link],http://ewhplcgugalr.com/

 

 

 

CwEQza <a href=”http://nrvruleqlzkb.com/“>nrvruleqlzkb</a>, [url=http://zxrvnontcoad.com/]zxrvnontcoad[/url], [link=http://rviimdvnfsks.com/]rviimdvnfsks[/link],http://hhhtbtjntazl.com/

 

 

 

Zp5IDA <a href=”http://sexpaiswvbtl.com/“>sexpaiswvbtl</a>, [url=http://eopsxakdtbeo.com/]eopsxakdtbeo[/url], [link=http://cgomoheqbduh.com/]cgomoheqbduh[/link],http://dfykdgnnejsp.com/

 

 

 

>>> from django.template import Template, Context
>>> t = Template('Your name is {{ name }}.')
>>> t.render(Context())
'Your name is .'
>>> t.render(Context({'var': 'hello'}))
'Your name is .'
>>> t.render(Context({'NAME': 'hello'}))
'Your name is .'
>>> t.render(Context({'Name': 'hello'}))
'Your name is .'

 

WCSlp6 <a href=”http://cxyqamrzlxvg.com/“>cxyqamrzlxvg</a>, [url=http://ebhuohogzrtz.com/]ebhuohogzrtz[/url], [link=http://kwaxjqjvaqzt.com/]kwaxjqjvaqzt[/link],http://facdqyqembtn.com/

 

 

 

oxzYAn <a href=”http://hwvajdodwjpf.com/“>hwvajdodwjpf</a>, [url=http://sesixfsygorj.com/]sesixfsygorj[/url], [link=http://tvmzczeaszag.com/]tvmzczeaszag[/link],http://kuymdzzlgwpj.com/

 

 

 

IjzstG <a href=”http://xllskwghqywz.com/“>xllskwghqywz</a>, [url=http://amlhqzrpxbuy.com/]amlhqzrpxbuy[/url], [link=http://mxpumocwixwz.com/]mxpumocwixwz[/link],http://fpaeymbrcquf.com/

 

 

 

多数时间,你可以通过传递一个完全填充(full populated)的字典给Context()上下文(Context) 。 但是初始化以后,你也可以从``上下文(Context)`` 对象添加或者删除条目,使用标准的Python字典语法(syntax):

 

 

 

>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'

5

DqLIrj <a href=”http://rdlnjcnkrmkf.com/“>rdlnjcnkrmkf</a>, [url=http://exrasrplzjkq.com/]exrasrplzjkq[/url], [link=http://zghjihmcltns.com/]zghjihmcltns[/link],http://lrypyraajwhe.com/

 

 

 

GDsgr3 <a href=”http://obbywvbberzn.com/“>obbywvbberzn</a>, [url=http://qtetslekfblc.com/]qtetslekfblc[/url], [link=http://myxlvewyihwn.com/]myxlvewyihwn[/link],http://xlckpcyxdfkj.com/

1

 

 

Fdpjeh <a href=”http://xsiyjiofkrrp.com/“>xsiyjiofkrrp</a>, [url=http://bcazhjlpaqrs.com/]bcazhjlpaqrs[/url], [link=http://rafzlbaozrdw.com/]rafzlbaozrdw[/link], http://rcdypauccfqp.com/

 

 

 

if/else

 

{%if%}{%if%}{%endif%} 之间的任何内容,例如:

 

{% if today_is_weekend %}
    <p>Welcome to the weekend!</p>
{% endif %}

1

{%else%} 标签是可选的:

 

{% if today_is_weekend %}
    <p>Welcome to the weekend!</p>
{% else %}
    <p>Get back to work.</p>
{% endif %}

 

Python 的“真值”

 

 

 

在python中空的列表 ([](){}''0None 对象,在逻辑判断中都为假,其他的情况都为真。

 

 

 

{%if%}andornotnot ),例如:

 

 

 

{% if athlete_list and coach_list %}
    Both athletes and coaches are available.
{% endif %}

{% if not athlete_list %}
    There are no athletes.
{% endif %}

{% if athlete_list or coach_list %}
    There are some athletes or some coaches.
{% endif %}

{% if not athlete_list or coach_list %}
    There are no athletes or there are some coaches. (OK, so
    writing English translations of Boolean logic sounds
    stupid; it's not our fault.)
{% endif %}

{% if athlete_list and not coach_list %}
    There are some athletes and absolutely no coaches.
{% endif %}

1

{%if%}andor ,因为逻辑上可能模糊的,例如,如下示例是错误的:

 

 

 

{% if athlete_list and coach_list or cheerleader_list %}

 

系统不支持用圆括号来组合比较操作。如果你发现需要组合操作,你可以考虑用逻辑语句来简化 模板的处理。例如,你需要组合andor{%if%} 标签,示例如下:

1

 

 

{% if athlete_list %}
    {% if coach_list or cheerleader_list %}
        We have athletes, and either coaches or cheerleaders!
    {% endif %}
{% endif %}

 

多次使用同一个逻辑操作符是没有问题的,但是我们不能把不同的操作符组合起来。比如这样的代码是没问题的:

 

 

 

{% if athlete_list or coach_list or parent_list or teacher_list %}

 

并没有{%elif%}{%if%} 标签来达成同样的效果:

 

 

 

{% if athlete_list %}
    <p>Here are the athletes: {{ athlete_list }}.</p>
{% else %}
    <p>No athletes are available.</p>
    {% if coach_list %}
        <p>Here are the coaches: {{ coach_list }}.</p>
    {% endif %}
{% endif %}

 

一定要用{%endif%}{%if%}TemplateSyntaxError 。

 

for

 

{%for%}forforXinY{%for%}{%endfor%} 之间的所有内容。

 

 

 

例如,给定一个运动员列表athlete_list 变量,我们可以使用下面的代码来显示这个列表:

1

 

 

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>

 

给标签增加一个reversed 使得该列表被反向迭代:

 

 

 

{% for athlete in athlete_list reversed %}
...
{% endfor %}

 

可以嵌套使用{%for%} 标签:

 

 

 

{% for country in countries %}
    <h1>{{ country.name }}</h1>
    <ul>
    {% for city in country.city_list %}
        <li>{{ city }}</li>
    {% endfor %}
    </ul>
{% endfor %}

 

Django不支持退出循环操作。如果我们想退出循环,可以改变正在迭代的变量,让其仅仅包含需要迭代的项目。同理,Django也不支持continue语句,我们无法让当前迭代操作跳回到循环头部。(请参看本章稍后的理念和限制小节,了解下决定这个设计的背后原因)

1

 

 

{%for%}forloop 模板变量。这个变量能提供一些当前循环进展的信息:

 

 

 

forloop.counterforloop.counter 将会被设置为1。例子如下:

 

{% for item in todo_list %}
    <p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}

 

forloop.counter0forloop.counter ,但是它是从0计数的。第一次执行循环时这个变量会被设置为0。

 

 

 

forloop.revcounterforloop.revcounter 将被设置为序列中项的总数。最后一次循环执行中,这个变量将被置1。

 

 

 

forloop.revcounter0forloop.revcounter ,但它以0做为结束索引。在第一次执行循环时,该变量会被置为序列的项的个数减1。在最后一次迭代时,该变量为0。

 

 

 

forloop.first 是一个布尔值。在第一次执行循环时该变量为True,在下面的情形中这个变量是很有用的。

 

 

 

{% for object in objects %}
    {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
    {{ object }}
    </li>
{% endfor %}

 

forloop.last 是一个布尔值;在最后一次执行循环时被置为True。一个常见的用法是在一系列的链接之间放置管道符(|)

 

 

 

{% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}

The above template code might output something like this::

        Link1 | Link2 | Link3 | Link4

5

forloop.parentloopforloop 对象的引用(在嵌套循环的情况下)。例子在此:

 

 

 

{% for country in countries %}
    <table>
    {% for city in country.city_list %}
        <tr>
        <td>Country #{{ forloop.parentloop.counter }}</td>
        <td>City #{{ forloop.counter }}</td>
        <td>{{ city }}</td>
        </tr>
    {% endfor %}
    </table>
{% endfor %}

1

forloop{%endfor%}forloop 就不可访问了。

 

 

 

Context和forloop变量

 

 

 

在一个{%for%}forloopforloop.parentloopforloop{%for%}forloop.parentloop 被重新命名。

 

ifequal/ifnotequal

 

Django模板系统压根儿就没想过实现一个全功能的编程语言,所以它不允许我们在模板中执行Python的语句(还是那句话,要了解更多请参看理念和限制小节)。但是比较两个变量的值并且显示一些结果实在是个太常见的需求了,所以Django提供了{%ifequal%} 标签供我们使用。

 

 

 

{%ifequal%}{%ifequal%}{%endifequal%} 之中所有的值。

 

 

 

下面的例子比较两个模板变量usercurrentuser :

 

 

 

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

 

参数可以是硬编码的字符串,随便用单引号或者双引号引起来,所以下列代码都是正确的:

1

 

 

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% endifequal %}

{% ifequal section "community" %}
    <h1>Community</h1>
{% endifequal %}

1

和{%if%}{%ifequal%}{%else%} 标签:

 

 

 

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

 

只有模板变量,字符串,整数和小数可以作为{%ifequal%} 标签的参数。这些是正确的例子:

 

 

 

{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}

 

其他的一些类型,例如Python的字典类型、列表类型、布尔类型,不能用在{%ifequal%} 中。 下面是些错误的例子:

 

 

 

{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}

 

如果你需要判断变量是真还是假,请使用{%if%}{%ifequal%} 。

 

注释

 

象HTML和其他的语言例如python一样,Django模板系统也允许注释。 注释使用{##} :

 

 

 

{# This is a comment #}

 

注释的内容不会在模板渲染时输出。

 

 

 

注释不能跨多行。这个限制是为了提高模板解析的性能。在下面这个模板中,输出结果和模板本身是 完全一样的(也就是说,注释标签并没有被解析为注释):

 

 

 

This is a {# this is not
a comment #}
test.

 

过滤器

 

就象本章前面提到的一样,模板过滤器是在变量被显示前修改它的值的一个简单方法。 过滤器看起来是这样的:

3

 

 

{{ name|lower }}

 

显示的内容是变量{{name}}lower| 来应用过滤器。

 

 

 

过滤器可以被 串联<p> 标签:

 

 

 

{{ my_text|escape|linebreaks }}

 

有些过滤器有参数。过滤器参数看起来是这样的:

 

 

 

{{ bio|truncatewords:"30" }}

 

这个将显示变量bio 的前30个词。过滤器参数总是使用双引号标识。

 

 

 

下面是一些最重要的过滤器;附录F有完整的过滤器列表。

 

 

 

addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。 这在处理包含JavaScript的文本时是非常有用的。

 

 

 

datedatedatetime 对象, 范例:

 

{{ pub_date|date:"F j, Y" }}

 

格式参数的定义在附录F中。

 

 

 

escapeescape 做下面这些转换:

 

  • 转换&&;
  • 转换<&lt; 
  • 转换>&gt; 
  • 转换"&quot; 
  • 转换'&#39; 

length__len__() 方法的对象)。

理念与局限

2

现在你已经对Django的模板语言有一些认识了,我们将指出一些特意设置的限制和为什么要这样做 背后的一些设计哲学。

 

 

 

相对Web应用中的其他组件,程序员们对模板系统的分歧是最大的。事实上,Python有成十上百的 开放源码的模板语言实现。每个实现都是因为开发者认为现存的模板语言不够用。(事实上,对一个 Python开发者来说,写一个自己的模板语言就象是某种“成人礼”一样!如果你还没有完成一个自己的 模板语言,好好考虑写一个,这是一个非常有趣的锻炼。)

 

 

 

明白了这个,你也许有兴趣知道事实上Django并不强制要求你必须使用它的模板语言。因为Django 虽然被设计成一个FULL-Stack的Web框架,它提供了开发者所必需的所有组件,而且在大多数情况 使用Django模板系统会比其他的Python模板库要更方便 一点,但是并不是严格要求你必须使用 它。就象你将在后续的章节中看到的一样,你也可以非常容易的在Django中使用其他的模板语言。

 

 

 

虽然如此,很明显,我们对Django模板语言的工作方式有着强烈的偏爱。这个模板语言来源于World Online的开发经验和Django创造者们集体智慧的结晶。下面是关于它的一些设计哲学理念:

 

 

 

业务逻辑应该和表现逻辑相对分开 。我们将模板系统视为控制表现及表现相关逻辑的工具,仅此而已。模板系统不应提供超出此基本目标的功能。

 

 

 

出于这个原因,在 Django 模板中是不可能直接调用 Python 代码的。所有的编程工作基本上都被局限于模板标签的能力范围。当然,  有可能写出自定义的模板标签来完成任意工作,但这些“超范围”的 Django 模板标签有意地不允许执行任何 Python 代码。

 

 

 

语法不应受到 HTML/XML 的束缚 。尽管 Django 模板系统主要用于生成 HTML,它还是被有意地设计为可生成非 HTML 格式,如纯文本。一些其它的模板语言是基于 XML 的,将所有的模板逻辑置于 XML 标签与属性之中,而 Django 有意地避开了这种限制。强制要求使用有效 XML 编写模板将会引发大量的人为错误和难以理解的错误信息,而且使用 XML 引擎解析模板也会导致令人无法容忍的模板处理开销。

 

 

 

假定设计师精通 HTML 编码 。模板系统的设计意图并不是为了让模板一定能够很好地显示在 Dreamweaver 这样的所见即所得编辑器中。这种限制过于苛刻,而且会使得语法不能像目前这样的完美。Django 要求模板创作人员对直接编辑 HTML 非常熟悉。

 

 

 

假定设计师不是 Python 程序员 设计师 程序员编写,因而假定这些人并不掌握 Python 相关知识。

 

 

 

当然,系统同样也特意地提供了对那些  Python 程序员进行模板制作的小型团队的支持。它提供了一种工作模式,允许通过编写原生 Python 代码进行系统语法拓展。(详见第十章)

 

 

 

Django架构的目标并不是要发明一种编程语言 。而是恰到好处地提供如分支和循环这一类编程式功能,这是进行与表现相关判断的基础。

 

采用这些设计理念的结果是导致 Django 模板语言有以下几点限制:

 

 

 

  • 模板中不能设置变量和改变变量的值 。可以通过编写自定义模板标签做到这一点(参见第十章),但正宗的 Django 模板标签做不到。 
  • 模板中不能调用任何的Python代码 。不存在转入 Python 模式或使用原生 Python 数据结构的方法。和前面一样,可以编写自定义模板标签来实现这个目标,但正統的 Django 模板标签做不到。1

在视图中使用模板

 

在学习了模板系统的基础之后,现在让我们使用相关知识来创建视图。重新打开我们在前一章在mysite.viewscurrent_datetime 视图。以下是其内容:

1

 

 

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

 

让我们用 Django 模板系统来修改该视图。第一步,你可能已经想到了要做下面这样的修改:

 

 

 

from django.template import Template, Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = Template("<html><body>It is now {{ current_date }}.</body></html>")
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

 

没错,它确实使用了模板系统,但是并没有解决我们在本章开头所指出的问题。也就是说,模板依然内嵌在 Python 代码之中。让我们将模板置于一个 单独的文件 中,并且让视图加载该文件来解决此问题。

2

 

 

你可能首先考虑把模板保存在文件系统的某个位置并用 Python 内建的文件操作函数来读取文件内容。假设文件保存在/home/djangouser/templates/mytemplate.html 中的话,代码就会像下面这样:

 

 

 

from django.template import Template, Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    # Simple way of using templates from the filesystem.
    # This doesn't account for missing files!
    fp = open('/home/djangouser/templates/mytemplate.html')
    t = Template(fp.read())
    fp.close()
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

 

然而,基于以下几个原因,该方法还算不上简洁:

1

 

 

  • 它没有对文件丢失的情况做出处理。如果文件mytemplate.htmlopen()IOError 异常。 
  • 这里对模板文件的位置进行了硬编码。如果你在每个视图函数都用该技术,就要不断复制这些模板的位置。更不用说还要带来大量的输入工作! 
  • 它包含了大量令人生厌的重复代码。与其在每次加载模板时都调用open()fp.read()fp.close() ,还不如做出更佳选择。 

要解决此问题,我们将使用 模板加载 模板目录 ,这是我们在接下来的章节中要讨论的两个话题。

 

模板加载

 

为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板,

 

 

 

要使用此模板加载API,首先你必须将模板的保存位置告诉框架。该项工作在 设置文件 中完成。

 

 

 

Django 设置文件是存放 Django 实例(也就是 Django 项目)配置的地方。它是一个简单的 Python 模块,其中包含了一些模块级变量,每个都是一项设置。

 

 

 

第二章中执行django-admin.pystartprojectmysitesettings.py 。查看一下该文件内容。其中包含如下变量(但并不一定是这个顺序):

 

 

 

DEBUG = True
TIME_ZONE = 'America/Chicago'
USE_I18N = True
ROOT_URLCONF = 'mysite.urls'

 

这里无需更多诠释;设置项与值均为简单的 Python 变量。同时由于配置文件只不过是纯 Python 模块,你可以完成一些动态工作,比如在设置某变量之前检查另一变量的值。(这也意味着你必须避免配置文件出现 Python 语法错误。)

 

 

 

我们将在附录 E 中详述配置文件,目前而言,仅需关注TEMPLATE_DIRSTEMPLATE_DIRS 中:

1

 

 

TEMPLATE_DIRS = (
    '/home/django/mysite/templates',
)

 

下面是一些注意事项:

 

 

 

你可以任意指定想要的目录,只要运行 Web 服务器的用户账号可以读取该目录的子目录和模板文件。如果实在想不出合适的位置来放置模板,我们建议在 Django 项目中创建一个templatesmysite 目录中)。

 

 

 

不要忘记模板目录字符串尾部的逗号!Python 要求单元素元组中必须使用逗号,以此消除与圆括号表达式之间的歧义。这是新手常犯的错误。

2

 

 

想避免此错误的话,你可以将列表而不是元组用作TEMPLATE_DIRS ,因为单元素列表并不强制要求以逗号收尾:

 

TEMPLATE_DIRS = [
    '/home/django/mysite/templates'
]

2

从语义上看,元组比列表略显合适(元组在创建之后就不能修改,而配置被读取以后就不应该有任何修改)。因此,我们推荐对TEMPLATE_DIRS 设置使用元组。

 

 

 

如果使用的是 Windows 平台,请包含驱动器符号并使用Unix风格的斜杠(/)而不是反斜杠(\),就像下面这样:

 

TEMPLATE_DIRS = (
    'C:/www/django/templates',
)

 

最省事的方式是使用绝对路径(即从文件系统根目录开始的目录路径)。如果想要更灵活一点并减少一些负面干扰,可利用 Django 配置文件就是 Python 代码这一点来动态构建TEMPLATE_DIRS 的内容,如:

 

import os.path

TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)

3

这个例子使用了神奇的 Python 内部变量__file__ ,该变量被自动设置为代码所在的 Python 模块文件名。

 

完成TEMPLATE_DIRScurrent_datetime 视图,进行如下修改:

 

 

 

from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

2

此范例中,我们使用了函数django.template.loader.get_template()get_template()Template 对象。

 

 

 

如果get_template()TemplateDoesNotExistpythonmanage.pyrunserverhttp://127.0.0.1:8000/time/current_datetimeDEBUGTruecurrent_datetime.htmlTemplateDoesNotExist 错误信息页面。

 

 

 

图 4-1: 无法找到模板时的出错页面

 

 

 

该页面与我们在第三章解释过的错误页面相似,只不过多了一块调试信息区:模板加载器事后检查区。该区域显示 Django 要加载哪个模板、每次尝试出错的原因(如:文件不存在等)。在调试模板加载错误时,这些信息的价值是不可估量的。

 

 

 

正如你从图 4-1 中的错误信息中所看到,Django 尝试通过组合TEMPLATE_DIRSget_template()TEMPLATE_DIRS'/home/django/templates''/home/django/templates/current_datetime.html'TEMPLATE_DIRS 包含多个目录,它将会查找每个目录直至找到模板或找遍所有目录。

 

 

 

接下来,在模板目录中创建包括以下模板代码current_datetime.html 文件:

 

 

 

<html><body>It is now {{ current_date }}.</body></html>

 

在网页浏览器中刷新该页,你将会看到完整解析后的页面。

 

 

 

render_to_response()

5

由于加载模板、填充contextHttpResponsedjango.shortcutsrender_to_response()render_to_response()ContextHttpResponse 对象。

 

 

 

下面就是使用render_to_response()current_datetime 范例。

1

 

 

from django.shortcuts import render_to_response
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    return render_to_response('current_datetime.html', {'current_date': now})

2

大变样了!让我们逐句看看代码发生的变化:

 

 

 

  • 我们不再需要导入get_templateTemplateContextHttpResponsedjango.shortcuts.render_to_responseimportdatetime 继续保留. 
  • 在current_datetimenowHttpResponserender_to_response()render_to_response()HttpResponsereturn 该值。 

render_to_response()Contextrender_to_response() 使用一个空字典。

 

locals() 技巧

 

思考一下我们对current_datetime 的最后一次赋值:

 

 

 

def current_datetime(request):
    now = datetime.datetime.now()
    return render_to_response('current_datetime.html', {'current_date': now})

 

很多时候,就像在这个范例中那样,你发现自己一直在计算某个变量,保存结果到变量中(比如:前面代码中的now  模板变量命名显得有点多余。不但多余,而且还要进行额外的键盘输入。

 

 

 

如果你是个喜欢偷懒的程序员并想让代码看起来更加简明,可以利用 Python 的内建函数locals() 。它返回的字典对所有局部变量的名称与值进行映射。因此,前面的视图可以重写成下面这个样子:

 

 

 

def current_datetime(request):
    current_date = datetime.datetime.now()
    return render_to_response('current_datetime.html', locals())

 

在此,我们没有像之前那样手工指定 context 字典,而是传入了locals()nowcurrent_datelocals() 并没有带来多 的改进,但是如果有多个模板变量要界定而你又想偷懒,这种技术可以减少一些键盘输入。

3

 

 

使用locals() 所有locals()request 。对此如何取舍取决你的应用程序。

 

 

 

最后要考虑的是在你调用locals() 时,Python 必须得动态创建字典,因此它会带来一点额外的开销。如果手动指定 context 字典,则可以避免这种开销。

 

get_template()中使用子目录

 

把所有的模板都存放在一个目录下可能会让事情变得难以掌控。你可能会考虑把模板存放在你模板目录的子目录中,这非常好。事实上,我们推荐这样做;一些Django的高级特性(例如将在第九章讲到的通用视图系统)的缺省约定就是期望使用这种模板布局。

 

 

 

把模板存放于模板目录的子目录中是件很轻松的事情。只需在调用get_template() 时,把子目录名和一条斜杠添加到模板名称之前,如:

 

 

 

t = get_template('dateapp/current_datetime.html')

 

由于render_to_response()get_template()render_to_response() 的第一个参数做相同处理。

 

 

 

对子目录树的深度没有限制,你想要多少层都可以。

 

 

 

注意

 

 

 

Windows用户必须使用斜杠而不是反斜杠。get_template() 假定的是 Unix 风格的文件名符号约定。

1

include 模板标签

 

在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签:{%include%}{%include%} 来减少重复。

 

 

 

下面这两个例子都包含了nav.html 模板。两个例子的作用完全相同,只不过是为了说明单、双引号都可以通用。

 

 

 

{% include 'nav.html' %}
{% include "nav.html" %}

 

下面的例子包含了includes/nav.html 模板的内容:

 

 

 

{% include 'includes/nav.html' %}

1

下面的例子包含了以变量template_name 的值为名称的模板内容:

 

 

 

{% include template_name %}

 

和在get_template()TEMPLATE_DIRS 的模板目录。

 

 

 

所包含的模板执行时的 context 和包含它们的模板是一样的。

 

 

 

如果未找到给定名称的模板文件,Django 会从以下两件事情中择一而为之:

 

 

 

  • 如果DEBUGTrueTemplateDoesNotExist 异常。 
  • 如果DEBUGFalse ,该标签不会引发错误信息,在标签位置不显示任何东西。 

模板继承

 

到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。这就带来一个常见的 Web 开发问题:在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?

 

 

 

解决该问题的传统做法是使用 服务器端的 includes{%include%} 支持了这种方法。但是用 Django 解决此类问题的首选方法是使用更加简洁的策略——模板继承 。

 

 

 

本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。

 

 

 

让我们通过修改current_datetime.htmlcurrent_datetime 创建一个更加完整的模板来体会一下这种做法:

 

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>The current time</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    <p>It is now {{ current_date }}.</p>

    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

1

这看起来很棒,但如果我们要为第三章的hours_ahead 视图创建另一个模板会发生什么事情呢?如果我们再次创建一个漂亮、有效且完整的 HTML 模板,我们可能会创建出下面这样的东西:

 

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>Future time</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>

    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

 

很明显,我们刚才重复了大量的 HTML 代码。想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。

 

 

 

解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。也许你会把模板头部的一些代码保存为header.html 文件:

 

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>

3

你可能会把底部保存到文件footer.html :

 

 

 

<hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

 

对基于 include 的策略,头部和底部的包含很简单。麻烦的是中间部分。在此范例中,每个页面都有一个<h1>Myhelpfultimestampsite</h1>header.html<title><h1><title> ,但这样又不允许在每个页面对它进行定制。何去何从呢?

 

 

 

Django 的模板继承系统解决了这些问题。你可以将其视为服务器端 include 的逆向思维版本。你可以对那些 不同 共同 代码段。

 

 

 

第一步是定义 基础模板 子模板 所继承。以下是我们目前所讲述范例的基础模板:

 

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <hr>
    <p>Thanks for visiting my site.</p>
    {% endblock %}
</body>
</html>

1

这个叫做base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。子模板的作用就是重载、添加或保留那些块的内容。(如果一直按我们的范例做话,请将此文件保存到模板目录。)

 

 

 

我们使用一个以前没有见过的模板标签:{%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), it will be {{ next_time }}.</p>
{% endblock %}

 

看起来很漂亮是不是?每个模板只包含对自己而言 独一无二base.html ,所有其它模板会立即反映出所作修改。

 

 

 

以下是其工作方式。在加载current_datetime.html{%extends%}base.html 。

 

 

 

此时,模板引擎注意到base.html{%block%}{blocktitle%}{%blockcontent%} 也是如此。

1

 

 

注意由于子模板并没有定义footer{%block%} 标签中的内容总是被当作一条退路。

 

 

 

继承并不改变 context 的工作方式,而且你可以按照需要使用多层继承。使用继承的一种常见方式是下面的三层法:

 

 

 

  1. 创建base.html 模板,在其中定义站点的主要外观感受。这些都是不常修改甚至从不修改的部分。 
  2. 为网站的每个区域创建base_SECTION.htmlbase_photos.htmlbase_forum.htmlbase.html 进行拓展,并包含区域特定的风格与设计。 
  3. 为每种类型的页面创建独立的模板,例如论坛页面或者图片库。这些模板拓展相应的区域模板。 

这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。

 

 

 

以下是使用模板继承的一些诀窍:

 

 

 

  • 如果在模板中使用{%extends%} ,必须保证其为模板中的第一个模板标记。否则,模板继承将不起作用。 
  • 一般来说,基础模板中的{%block%} 标签越多越好。记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。俗话说,钩子越多越好。1
  • 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个{%block%} 中。 
  • 如果需要获得父模板中代码块的内容,可以使用{{block.super}} 变量。如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。 
  • 不可同一个模板中定义多个同名的{%block%} {%block%} 标签,父模板将无从得知要使用哪个块的内容。 
  • {%extends%}get_template()TEMPLATE_DIRS 设置之后。 
  • 多数情况下,{%extends%} 的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量。这使得你能够实现一些很酷的动态功能。 

接下来?

 

时下大多数网站都是 数据库驱动 的:网站的内容都是存储在关系型数据库中。这使得数据和逻辑能够彻底地分开(视图和模板也以同样方式对逻辑和显示进行了分隔。)

 

 

 

在下一章里将讲述 Django 提供的数据库交互工具。