這會(huì)導(dǎo)致幾個(gè)問(wèn)題:
1,顯然,任何頁(yè)面的改動(dòng)會(huì)牽扯到Python代碼的改動(dòng)
網(wǎng)站的設(shè)計(jì)改動(dòng)會(huì)比Python代碼改動(dòng)更頻繁,所以如果我們將兩者分離開(kāi)會(huì)更方便
2,其次,寫(xiě)后臺(tái)Python代碼與設(shè)計(jì)HTML是不同的工作,更專(zhuān)業(yè)的Web開(kāi)發(fā)應(yīng)該將兩者分開(kāi)
頁(yè)面設(shè)計(jì)者和HTML/CSS程序員不應(yīng)該編輯Python代碼,他們應(yīng)該與HTML打交道
3,程序員寫(xiě)Python代碼同時(shí)頁(yè)面設(shè)計(jì)者寫(xiě)HTML模板會(huì)更高效,而不是一個(gè)人等待另一個(gè)人編輯同樣的文件
因此,使用Django的模板系統(tǒng)分離設(shè)計(jì)和Python代碼會(huì)更干凈更易維護(hù)
模板系統(tǒng)基礎(chǔ)
Django模板是一個(gè)string文本,它用來(lái)分離一個(gè)文檔的展現(xiàn)和數(shù)據(jù)
模板定義了placeholder和表示多種邏輯的tags來(lái)規(guī)定文檔如何展現(xiàn)
通常模板用來(lái)輸出HTML,但是Django模板也能生成其它基于文本的形式
讓我們來(lái)看看一個(gè)簡(jiǎn)單的模板例子:
- <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>
這個(gè)模板本質(zhì)上是HTML,但是夾雜了一些變量和模板標(biāo)簽:
1,用{{}}包圍的是變量,如{{person_name}},這表示把給定變量的值插入,如何指定這些變量的值我們即將說(shuō)明
2,用{%%}包圍的是塊標(biāo)簽,如{%if ordered_warranty%}
塊標(biāo)簽的含義很豐富,它告訴模板系統(tǒng)做一些事情
在這個(gè)例子模板中包含兩個(gè)塊標(biāo)簽:for標(biāo)簽表現(xiàn)為一個(gè)簡(jiǎn)單的循環(huán)結(jié)構(gòu),讓你按順序遍歷每條數(shù)據(jù)
if標(biāo)簽則表現(xiàn)為邏輯的if語(yǔ)句
在這里,上面的標(biāo)簽檢查ordered_warranty變量的值是否為T(mén)rue
如果是True,模板系統(tǒng)會(huì)顯示{%if ordered_warranty%}和{%endif%}之間的內(nèi)容
否則,模板系統(tǒng)不會(huì)顯示這些內(nèi)容
模板系統(tǒng)也支持{%else%}等其它邏輯語(yǔ)句
3,上面還有一個(gè)過(guò)濾器的例子,過(guò)濾器是改變變量顯示的方式
上面的例子中{{ship_date|date:"F j, Y"}}把ship_date變量傳遞給過(guò)濾器
并給date過(guò)濾器傳遞了一個(gè)參數(shù)“F j, Y”,date過(guò)濾器以給定參數(shù)的形式格式化日期
類(lèi)似于Unix,過(guò)濾器使用管道字符“|”
Django模板支持多種內(nèi)建的塊標(biāo)簽,并且你可以寫(xiě)你自己的標(biāo)簽
使用模板系統(tǒng)
在Python代碼中使用模板系統(tǒng),請(qǐng)按照下面的步驟:
1,用模板代碼創(chuàng)建一個(gè)Template對(duì)象
Django也提供指定模板文件路徑的方式創(chuàng)建Template對(duì)象
2,使用一些給定變量context調(diào)用Template對(duì)象的render()方法
這將返回一個(gè)完全渲染的模板,它是一個(gè)string,其中所有的變量和塊標(biāo)簽都會(huì)根據(jù)context得到值
創(chuàng)建模板對(duì)象
最簡(jiǎn)單的方式是直接初始化它,Template類(lèi)在django.template模塊中,初始化方法需要一個(gè)參數(shù)
下面進(jìn)入Python交互環(huán)境看看:
- >>> from django.template import Template
- >>> t = Template("My name is {{my_name}}.")
- >>> print t
你將看到如下信息
- <django.template.Template object at 0xb7d5f24c>
0xb7d5f24c每次都會(huì)改變,但是無(wú)所謂,它是Template對(duì)象的Python“identity”
在這本書(shū)中,我們會(huì)在Python的交互式會(huì)話環(huán)境中展示一些示例。當(dāng)你看到三個(gè)大于號(hào)'>>>',就可以確定是在交互環(huán)境中了。
如果你從本書(shū)中拷貝代碼,記得不要拷貝這些大于號(hào)。
當(dāng)你創(chuàng)建Template對(duì)象,模板系統(tǒng)會(huì)編譯模板代碼,并準(zhǔn)備渲染
如果你的模板代碼有語(yǔ)法錯(cuò)誤,調(diào)用Template()方法會(huì)觸發(fā)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'
系統(tǒng)觸發(fā)TemplateSyntaxError異常可能出于以下情況:
1,不合法的塊標(biāo)簽
2,合法塊標(biāo)簽接受不合法的參數(shù)
3,不合法的過(guò)濾器
4,合法過(guò)濾器接受不合法的參數(shù)
5,不合法的模板語(yǔ)法
6,塊標(biāo)簽沒(méi)關(guān)
渲染模板
一旦你擁有一個(gè)Template對(duì)象,你可以通過(guò)給一個(gè)context來(lái)給它傳遞數(shù)據(jù)
context是一個(gè)變量及賦予的值的集合,模板使用它來(lái)得到變量的值,或者對(duì)于塊標(biāo)簽求值
這個(gè)context由django.template模塊的Context類(lèi)表示
它的初始化函數(shù)有一個(gè)可選的參數(shù):一個(gè)映射變量名和變量值的字典
通過(guò)context調(diào)用Template對(duì)象的render()方法來(lái)填充模板,例如:
- >>> from django.template import Context, Template
- >>> t = Template("My name is {{name}}.")
- >>> c = Context({"name": "Stephane"})
- >>> t.render(c)
- 'My name is Stephane.'
變量名必須以字母(A-Z或a-z)開(kāi)始,可以包含數(shù)字,下劃線和小數(shù)點(diǎn),變量名大小寫(xiě)敏感
下面是一個(gè)模板編譯和渲染的例子,使用這章開(kāi)始時(shí)的模板例子:
- >>> 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 to ship on April 2, 2009.</p>\n\n<p>Your warranty information will be included
- in the packaging.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment</p>"
讓我們來(lái)看看都做了些什么:
1,我們import Template和Context類(lèi),它們都在django.template模塊里面
2,我們把模板文本存儲(chǔ)在raw_template變量里,我們使用"""來(lái)構(gòu)建string,它可以跨越多行
3,我們創(chuàng)建模板對(duì)象t,并給Template類(lèi)的初始化函數(shù)傳遞raw_template變量
4,我們從Python的標(biāo)準(zhǔn)庫(kù)import datetime模塊,下面會(huì)用到它
5,我們創(chuàng)建一個(gè)context對(duì)象c,它的初始化函數(shù)使用一個(gè)映射變量名和變量值的字典
例如我們指定person_name的值為'John Smith',product的值為'Super Lawn Mower'等等
6,最后,我們調(diào)用模板對(duì)象的render()方法,參數(shù)為context對(duì)象c
這將返回渲染后的模板,將模板中的變量值替換并計(jì)算塊標(biāo)簽的結(jié)果
如果你剛接觸Python,你可能會(huì)問(wèn)為什么輸出中包含了新行字符'\n'而不是換行
這是因?yàn)镻ython交互環(huán)境中調(diào)用t.render(c)會(huì)顯示string的representation而不是string的值
如果你想看到換行而不是'\n',使用print t.render(c)即可
上面是使用Django模板系統(tǒng)的基礎(chǔ),只是創(chuàng)建一個(gè)模板對(duì)象和context對(duì)象然后調(diào)用render()方法
同一個(gè)模板,多個(gè)context的情況:
一旦你創(chuàng)建了一個(gè)模板對(duì)象,你可以渲染多個(gè)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
無(wú)論何時(shí),你使用同一個(gè)模板來(lái)渲染多個(gè)context的話,創(chuàng)建一次Template對(duì)象然后調(diào)用render()多次會(huì)更高效
- # 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}))
Django的模板解析非常快,在后臺(tái),大部分的解析通過(guò)一個(gè)單獨(dú)的對(duì)正則表達(dá)式的調(diào)用來(lái)做
這與基于XML的模板引擎形成鮮明對(duì)比,XML解析器比Django的模板渲染系統(tǒng)慢很多
Context變量查找
上面的例子中,我們給模板context傳遞了簡(jiǎn)單的值,大部分是string,以及一個(gè)datetime.date
盡管如此,模板系統(tǒng)優(yōu)雅的處理更復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如列表,字典和自定義對(duì)象
在Django模板系統(tǒng)中處理復(fù)雜數(shù)據(jù)結(jié)構(gòu)的關(guān)鍵是使用(.)字符
使用小數(shù)點(diǎn)來(lái)得到字典的key,屬性,對(duì)象的索引和方法
下面通過(guò)例子來(lái)解釋?zhuān)ㄟ^(guò)(.)訪問(wèn)字典的key:
- >>> 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.'
通過(guò)(.)來(lái)訪問(wèn)對(duì)象的屬性:
- >>> 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.'
下面的例子使用一個(gè)自定義類(lèi):
- >>> 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.'
小數(shù)點(diǎn)也可以用來(lái)調(diào)用列表的索引:
- >>> from django.template import Template, Context
- >>> t = Template('Item 2 is {{ items.2 }}.')
- >>> c = Contexst({'items': ['apples', 'bananas', 'carrots']})
- >>> t.render(c)
- 'Item 2 is carrots.'
負(fù)數(shù)的列表索引是不允許的,例如模板變量{{ items.-1 }}將觸發(fā)TemplateSyntaxError
最后小數(shù)點(diǎn)還可以用來(lái)訪問(wèn)對(duì)象的方法,例如Python的string有upper()和isdigit()方法:
- >>> 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'
注意,調(diào)用方法時(shí)你不能加括號(hào),你也不能給方法傳遞參數(shù)
你只能調(diào)用沒(méi)有參數(shù)的方法,后面我們會(huì)解釋這些
總結(jié)一下,當(dāng)模板系統(tǒng)遇到變量名里有小數(shù)點(diǎn)時(shí)會(huì)按以下順序查找:
1,字典查找,如foo["bar"]
2,屬性查找,如foo.bar
3,方法調(diào)用,如foo.bar()
3,列表的索引查找,如foo[bar]
小數(shù)點(diǎn)可以多級(jí)縱深查詢,例如{{ person.name.upper }}表示字典查詢person['name']然后調(diào)用upper()方法
- >>> 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.'
關(guān)于方法調(diào)用
方法調(diào)用要比其他的查詢稍微復(fù)雜一點(diǎn),下面是需要記住的幾點(diǎn):
1,在方法查詢的時(shí)候,如果一個(gè)方法觸發(fā)了異常,這個(gè)異常會(huì)傳遞從而導(dǎo)致渲染失
敗,但是如果異常有一個(gè)值為T(mén)rue的silent_variable_failure屬性,這個(gè)變量會(huì)渲染成空string:
- >>> t = Template("My name is {{ person.first_name }}.")
- >>> class PersonClas3:
- ... def first_name(self):
- ... raise AssertionError, "foo"
- >>> p = PersonClass3()
- >>> t.render(Context({"person": p}))
- Traceback (most recent call last):
- ...
- AssertionError: foo
- >>> class SilentAssetionError(AssertionError):
- ... silent_variable_failure = True
- >>> class PersonClass4:
- ... def first_name(self):
- ... raise SilentAssertionError
- >>> p = PersonClass4()
- >>> t.render(Context({"person": p}))
- "My name is ."
2,方法調(diào)用僅僅在它沒(méi)有參數(shù)時(shí)起作用,否則系統(tǒng)將繼續(xù)查找下一個(gè)類(lèi)型(列表索引查詢)
3,顯然一些方法有副作用,讓系統(tǒng)訪問(wèn)它們是很愚蠢的,而且很可能會(huì)造成安全性問(wèn)
題。
例如你有一個(gè)BankAccount對(duì)象,該對(duì)象有一個(gè)delete()方法,模板系統(tǒng)不應(yīng)該允許做下面的事情
I will now delete this valuable data. {{ account.delete }}
為了防止這種狀況,可以在方法里設(shè)置一個(gè)方法屬性alters_data
如果設(shè)置了alters_data=True的話模板系統(tǒng)就不會(huì)執(zhí)行這個(gè)方法:
- def delete(self):
- # Delete the account
- delete.alters_data = True
不合法的變量怎樣處理
默認(rèn)情況下如果變量不存在,模板系統(tǒng)會(huì)把它渲染成空string,例如:
- >>> 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 .'
系統(tǒng)會(huì)靜悄悄地顯示錯(cuò)誤的頁(yè)面,而不是產(chǎn)生一個(gè)異常,因?yàn)檫@種情況通常是人為的錯(cuò)誤。
在現(xiàn)實(shí)情形下,一個(gè)web站點(diǎn)因?yàn)橐粋(gè)模板代碼語(yǔ)法的錯(cuò)誤而變得不可用是不可接受的。
我們可以通過(guò)設(shè)置Django配置更改Django的缺省行為,第10章擴(kuò)展模板引擎會(huì)我們會(huì)討論這個(gè)
玩玩Context對(duì)象
大多數(shù)情況下你初始化Context對(duì)象會(huì)傳遞一個(gè)字典給Context()
一旦你初始化了Context,你可以使用標(biāo)準(zhǔn)Python字典語(yǔ)法增減Context對(duì)象的items:
- >>> from django.template import Context
- >>> c = Context({"foo": "bar"})
- >>> c['foo']
- 'bar'
- >>> del c['foo']
- >>> c['foo']
- ''
- >>> c['newvariable'] = 'hello'
- >>> c['newvariable']
- 'hello'
Context對(duì)象是一個(gè)stack,你可以push()和pop()
如果你pop()的太多的話它將觸發(fā)django.template.ContextPopException:
- >>> c = Context()
- >>> c['foo'] = 'first level'
- >>> c.push()
- >>> c['foo'] = 'second level'
- >>> c['foo']
- 'second level'
- >>> c.pop()
- >>> c['foo']
- 'first level'
- >>> c['foo'] = 'overwritten'
- >>> c['foo']
- 'overwritten'
- >>> c.pop()
- Traceback (most recent call last):
- ...
- django.template.ContextPopException
第10章你會(huì)看到使用Context作為stack自定義模板標(biāo)簽
模板標(biāo)簽和過(guò)濾器基礎(chǔ)
我們已經(jīng)提到模板系統(tǒng)使用內(nèi)建的標(biāo)簽和過(guò)濾器
這里我們看看常見(jiàn)的,附錄6包含了完整的內(nèi)建標(biāo)簽和過(guò)濾器,你自己熟悉那個(gè)列表來(lái)了解可以做什么是個(gè)好主意
if/else
{% if %}標(biāo)簽計(jì)算一個(gè)變量值,如果是“true”,即它存在、不為空并且不是false的boolean值
系統(tǒng)則會(huì)顯示{% if %}和{% endif %}間的所有內(nèi)容:
- {% if today_is_weekend %}
- <p>Welcome to the weekend!</p>
- {% else %}
- <p>Get back to work.</p>
- {% endif %}
{% if %}標(biāo)簽接受and,or或者not來(lái)測(cè)試多個(gè)變量值或者否定一個(gè)給定的變量,例如:
- {% 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.
- {% endif %}
- {% if athlete_list and not coach_list %}
- There are some athletes and absolutely no coaches.
- {% endif %}
{% if %}標(biāo)簽不允許同一標(biāo)簽里同時(shí)出現(xiàn)and和or,否則邏輯容易產(chǎn)生歧義,例如下面的標(biāo)簽是不合法的:
- {% if athlete_list and coach_list or cheerleader_list %}
如果你想結(jié)合and和or來(lái)做高級(jí)邏輯,只需使用嵌套的{% if %}標(biāo)簽即可:
- {% if athlete_list %}
- {% if coach_list or cheerleader_list %}
- We have athletes, and either coaches or cheerleaders!
- {% endif %}
- {% endif %}
多次使用同一個(gè)邏輯符號(hào)是合法的:
- {% if athlete_list or coach_list or parent_list or teacher_list %}
沒(méi)有{% elif %}標(biāo)簽,使用嵌套的{% if %}標(biāo)簽可以做到同樣的事情:
- {% 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 %}
確認(rèn)使用{% endif %}來(lái)關(guān)閉{% if %}標(biāo)簽,否則Django觸發(fā)TemplateSyntaxError
for
{% for %}標(biāo)簽允許你按順序遍歷一個(gè)序列中的各個(gè)元素
Python的for語(yǔ)句語(yǔ)法為for X in Y,X是用來(lái)遍歷Y的變量
每次循環(huán)模板系統(tǒng)都會(huì)渲染{% for %}和{% endfor %}之間的所有內(nèi)容
例如,顯示給定athlete_list變量來(lái)顯示athlete列表:
- <ul>
- {% for athlete in athlete_list %}
- <li>{{ athlete.name }}</li>
- {% endfor %}
- </ul>
在標(biāo)簽里添加reversed來(lái)反序循環(huán)列表:
- {% for athlete in athlete_list reversed %}
- ...
- {% endfor %}
- {% for %}標(biāo)簽可以嵌套:
- {% for country in countries %}
- <h1>{{ country.name }}</h1>
- <ul>
- {% for city in country.city_list %}
- <li>{{ city }}</li>
- {% endfor %}
- </ul>
- {% endfor %}
系統(tǒng)不支持中斷循環(huán),如果你想這樣,你可以改變你想遍歷的變量來(lái)使得變量只包含你想遍歷的值
類(lèi)似的,系統(tǒng)也不支持continue語(yǔ)句,本章后面的“哲學(xué)和限制”會(huì)解釋設(shè)計(jì)的原則
{% for %}標(biāo)簽內(nèi)置了一個(gè)forloop模板變量,這個(gè)變量含有一些屬性可以提供給你一些關(guān)于循環(huán)的信息
1,forloop.counter表示循環(huán)的次數(shù),它從1開(kāi)始計(jì)數(shù),第一次循環(huán)設(shè)為1,例如:
- {% for item in todo_list %}
- <p>{{ forloop.counter }}: {{ item }}</p>
- {% endfor %}
2,forloop.counter0類(lèi)似于forloop.counter,但它是從0開(kāi)始計(jì)數(shù),第一次循環(huán)設(shè)為0
3,forloop.revcounter表示循環(huán)中剩下的items數(shù)量,第一次循環(huán)時(shí)設(shè)為items總數(shù),最后一次設(shè)為1
4,forloop.revcounter0類(lèi)似于forloop.revcounter,但它是表示的數(shù)量少一個(gè),即最后一次循環(huán)時(shí)設(shè)為0
5,forloop.first當(dāng)?shù)谝淮窝h(huán)時(shí)值為T(mén)rue,在特別情況下很有用
安徽新華電腦學(xué)校專(zhuān)業(yè)職業(yè)規(guī)劃師為你提供更多幫助【在線咨詢】