Django模板系統(很是詳細)

翻譯www.djangobook.com之第四章:Django模板系統

The Django Book:第4章 Django模板系統

revised by  xin_wang

前面的章節咱們看到如何在視圖中返回HTML,可是HTML是硬編碼在Python代碼中的
這會致使幾個問題:
1,顯然,任何頁面的改動會牽扯到Python代碼的改動
網站的設計改動會比Python代碼改動更頻繁,因此若是咱們將二者分離開會更方便
2,其次,寫後臺Python代碼與設計HTML是不一樣的工做,更專業的Web開發應該將二者分開
頁面設計者和HTML/CSS程序員不該該編輯Python代碼,他們應該與HTML打交道
3,程序員寫Python代碼同時頁面設計者寫HTML模板會更高效,而不是一我的等待另外一我的編輯一樣的文件
所以,使用Django的模板系統分離設計和Python代碼會更乾淨更易維護

模板系統基礎
Django模板是一個string文本,它用來分離一個文檔的展示和數據
模板定義了placeholder和表示多種邏輯的tags來規定文檔如何展示
一般模板用來輸出HTML,可是Django模板也能生成其它基於文本的形式
讓咱們來看看一個簡單的模板例子:
[java]  view plain  copy
 
  1. <html>  
  2. <head><title>Ordering notice</title></head>  
  3. <body>  
  4. <p>Dear {{ person_name }},</p>  
  5. <p>Thanks for placing an order from {{ company }}. It's scheduled to  
  6. ship on {{ ship_date|date:"F j, Y" }}.</p>  
  7. <p>Here are the items you've ordered:</p>  
  8. <ul>  
  9. {% for item in item_list %}  
  10. <li>{{ item }}</li>  
  11. {% endfor %}  
  12. </ul>  
  13. {% if ordered_warranty %}  
  14. <p>Your warranty information will be included in the packaging.</p>  
  15. {% endif %}  
  16. <p>Sincerely,<br />{{ company }}</p>  
  17. </body>  
  18. </html>  

這個模板本質上是HTML,可是夾雜了一些變量和模板標籤:
1,用{{}}包圍的是變量,如{{person_name}},這表示把給定變量的值插入,如何指定這些變量的值咱們即將說明
2,用{%%}包圍的是塊標籤,如{%if ordered_warranty%}
塊標籤的含義很豐富,它告訴模板系統作一些事情
在這個例子模板中包含兩個塊標籤:for標籤表現爲一個簡單的循環結構,讓你按順序遍歷每條數據
if標籤則表現爲邏輯的if語句
在這裏,上面的標籤檢查ordered_warranty變量的值是否爲True
若是是True,模板系統會顯示{%if ordered_warranty%}和{%endif%}之間的內容
不然,模板系統不會顯示這些內容
模板系統也支持{%else%}等其它邏輯語句
3,上面還有一個過濾器的例子,過濾器是改變變量顯示的方式
上面的例子中{{ship_date|date:"F j, Y"}}把ship_date變量傳遞給過濾器
並給date過濾器傳遞了一個參數「F j, Y」,date過濾器以給定參數的形式格式化日期
相似於Unix,過濾器使用管道字符「|」
Django模板支持多種內建的塊標籤,而且你能夠寫你本身的標籤

使用模板系統
在Python代碼中使用模板系統,請按照下面的步驟:
1,用模板代碼建立一個Template對象
Django也提供指定模板文件路徑的方式建立Template對象
2,使用一些給定變量context調用Template對象的render()方法
這將返回一個徹底渲染的模板,它是一個string,其中全部的變量和塊標籤都會根據context獲得值

建立模板對象
最簡單的方式是直接初始化它,Template類在django.template模塊中,初始化方法須要一個參數
下面進入Python交互環境看看:
[java]  view plain  copy
 
  1. >>> from django.template import Template  
  2. >>> t = Template("My name is {{my_name}}.")  
  3. >>> print t  

你將看到以下信息
[java]  view plain  copy
 
  1. <django.template.Template object at 0xb7d5f24c>  

0xb7d5f24c每次都會改變,可是無所謂,它是Template對象的Python「identity」
在這本書中,咱們會在Python的交互式會話環境中展現一些示例。當你看到三個大於號'>>>',就能夠肯定是在交互環境中了。
若是你從本書中拷貝代碼,記得不要拷貝這些大於號。
當你建立Template對象,模板系統會編譯模板代碼,並準備渲染
若是你的模板代碼有語法錯誤,調用Template()方法會觸發TemplateSyntaxError異常
[java]  view plain  copy
 
  1. >>> from django.template import Template  
  2. >>> t = Template('{%notatag%}')  
  3. Traceback (most recent call last):  
  4.     File "<stdin>", line 1, in ?  
  5.     ...  
  6.    django.template.TemplateSyntaxError: Invalid block tag: 'notatag'  

系統觸發TemplateSyntaxError異常可能出於如下狀況:
1,不合法的塊標籤
2,合法塊標籤接受不合法的參數
3,不合法的過濾器
4,合法過濾器接受不合法的參數
5,不合法的模板語法
6,塊標籤沒關

渲染模板
一旦你擁有一個Template對象,你能夠經過給一個context來給它傳遞數據
context是一個變量及賦予的值的集合,模板使用它來獲得變量的值,或者對於塊標籤求值
這個context由django.template模塊的Context類表示
它的初始化函數有一個可選的參數:一個映射變量名和變量值的字典
經過context調用Template對象的render()方法來填充模板,例如:
[java]  view plain  copy
 
  1. >>> from django.template import Context, Template  
  2. >>> t = Template("My name is {{name}}.")  
  3. >>> c = Context({"name": "Stephane"})  
  4. >>> t.render(c)  
  5. 'My name is Stephane.'  

變量名必須以字母(A-Z或a-z)開始,能夠包含數字,下劃線和小數點,變量名大小寫敏感
下面是一個模板編譯和渲染的例子,使用這章開始時的模板例子:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> raw_template = """<p>Dear {{ person_name }},</p>  
  3. ...  
  4. ... <p>Thanks for ordering {{ product }} from {{ company }}. It's scheduled to  
  5. ... ship on {{ ship_date|date:"F j, Y" }}.</p>  
  6. ...  
  7. ... {% if ordered_warranty %}  
  8. ... <p>Your warranty information will be included in the packaging.</p>  
  9. ... {% endif %}  
  10. ...  
  11. ... <p>Sincerely,<br />{{ company }}</p>"""  
  12. >>> t = Template(raw_template)  
  13. >>> import datetime  
  14. >>> c = Context({'person_name': 'John Smith',  
  15. ...     'product': 'Super Lawn Mower',  
  16. ...     'company': 'Outdoor Equipment',  
  17. ...     'ship_date': datetime.date(2009, 4, 2),  
  18. ...     'ordered_warranty': True})  
  19. >>> t.render(c)  
  20. "<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from Outdoor Equipment.  
  21. It's scheduled to ship on April 2, 2009.</p>\n\n<p>Your warranty information will be included  
  22. in the packaging.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment</p>"  

讓咱們來看看都作了些什麼:
1,咱們import Template和Context類,它們都在django.template模塊裏面
2,咱們把模板文本存儲在raw_template變量裏,咱們使用"""來構建string,它能夠跨越多行
3,咱們建立模板對象t,並給Template類的初始化函數傳遞raw_template變量
4,咱們從Python的標準庫import datetime模塊,下面會用到它
5,咱們建立一個context對象c,它的初始化函數使用一個映射變量名和變量值的字典
例如咱們指定person_name的值爲'John Smith',product的值爲'Super Lawn Mower'等等
6,最後,咱們調用模板對象的render()方法,參數爲context對象c
這將返回渲染後的模板,將模板中的變量值替換並計算塊標籤的結果
若是你剛接觸Python,你可能會問爲何輸出中包含了新行字符'\n'而不是換行
這是由於Python交互環境中調用t.render(c)會顯示string的representation而不是string的值
若是你想看到換行而不是'\n',使用print t.render(c)便可
上面是使用Django模板系統的基礎,只是建立一個模板對象和context對象而後調用render()方法
同一個模板,多個context的狀況:
一旦你建立了一個模板對象,你能夠渲染多個context,例如:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> t = Template('Hello, {{ name }}')  
  3. >>> print t.render(Context({'name': 'John'}))  
  4. Hello, John  
  5. >>> print t.render(Context({'name': 'Julie'}))  
  6. Hello, Julie  
  7. >>> print t.render(Context({'name': 'Pat'}))  
  8. Hello, Pat  

不管什麼時候,你使用同一個模板來渲染多個context的話,建立一次Template對象而後調用render()屢次會更高效
[java]  view plain  copy
 
  1. # Bad  
  2. for name in ('John', 'Julie', 'Pat'):  
  3.     t = Template('Hello, {{ name }}')  
  4.     print t.render(Context({'name': name}))  
  5. # Good  
  6. t = Template('Hello, {{ name }}')  
  7. for name in ('John', 'Julie', 'Pat'):  
  8.     print t.render(Context({'name': name}))  

Django的模板解析很是快,在後臺,大部分的解析經過一個單獨的對正則表達式的調用來作
這與基於XML的模板引擎造成鮮明對比,XML解析器比Django的模板渲染系統慢不少

Context變量查找
上面的例子中,咱們給模板context傳遞了簡單的值,大部分是string,以及一個datetime.date
儘管如此,模板系統優雅的處理更復雜的數據結構,如列表,字典和自定義對象
在Django模板系統中處理複雜數據結構的關鍵是使用(.)字符
使用小數點來獲得字典的key,屬性,對象的索引和方法
下面經過例子來解釋,經過(.)訪問字典的key:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> person = {'name': 'Sally', 'age': '43'}  
  3. >>> t = Template('{{ person.name }} is {{ person.age }} years old.')  
  4. >>> c= Context({'person': person})  
  5. >>> t.render(c)  
  6. 'Sally is 43 years old.'  

經過(.)來訪問對象的屬性:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> import datetime  
  3. >>> d = datetime.date(1993, 5, 2)  
  4. >>> d.year  
  5. 1993  
  6. >>> d.month  
  7. 5  
  8. >>> d.day  
  9. 2  
  10. >>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')  
  11. >>> c = Context({'date': d})  
  12. >>> t.render(c)  
  13. 'The month is 5 and the year is 1993.'  

下面的例子使用一個自定義類:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> class Person(object):  
  3. ...    def __init__(self, first_name, last_name):  
  4. ...        self.first_name, self.last_name = first_name, last_name  
  5. >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')  
  6. >>> c = Context({'person': Person('John', 'Smith')})  
  7. >>> t.render(c)  
  8. 'Hello, John Smith.'  

小數點也能夠用來調用列表的索引:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> t = Template('Item 2 is {{ items.2 }}.')  
  3. >>> c = Contexst({'items': ['apples', 'bananas', 'carrots']})  
  4. >>> t.render(c)  
  5. 'Item 2 is carrots.'  

負數的列表索引是不容許的,例如模板變量{{ items.-1 }}將觸發TemplateSyntaxError
最後小數點還能夠用來訪問對象的方法,例如Python的string有upper()和isdigit()方法:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> t = Template('{{ var }} -- {{var.upper }} -- {{ var.isdigit }}')  
  3. >>> t.render(Context({'var': 'hello'}))  
  4. 'hello -- HELLO -- False'  
  5. >>> t.render(Context({'var': '123'}))  
  6. '123 - 123 - True'  

注意,調用方法時你不能加括號,你也不能給方法傳遞參數
你只能調用沒有參數的方法,後面咱們會解釋這些
總結一下,當模板系統遇到變量名裏有小數點時會按如下順序查找:
1,字典查找,如foo["bar"]
2,屬性查找,如foo.bar
3,方法調用,如foo.bar()
3,列表的索引查找,如foo[bar]
小數點能夠多級縱深查詢,例如{{ person.name.upper }}表示字典查詢person['name']而後調用upper()方法
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> person = {'name': 'Sally', 'age': '43'}  
  3. >>> t = Template('{{ person.name.upper }} is {{ person.age }} years old.')  
  4. >>> c = Context({'person': person})  
  5. >>> t.render(c)  
  6. 'SALLY is 43 years old.'  


關於方法調用
方法調用要比其餘的查詢稍微複雜一點,下面是須要記住的幾點:
1,在方法查詢的時候,若是一個方法觸發了異常,這個異常會傳遞從而致使渲染失
敗,可是若是異常有一個值爲True的silent_variable_failure屬性,這個變量會渲染成空string:
[java]  view plain  copy
 
  1. >>> t = Template("My name is {{ person.first_name }}.")  
  2. >>> class PersonClas3:  
  3. ...     def first_name(self):  
  4. ...         raise AssertionError, "foo"  
  5. >>> p = PersonClass3()  
  6. >>> t.render(Context({"person": p}))  
  7. Traceback (most recent call last):  
  8. ...  
  9. AssertionError: foo  
  10. >>> class SilentAssetionError(AssertionError):  
  11. ...     silent_variable_failure = True  
  12. >>> class PersonClass4:  
  13. ...     def first_name(self):  
  14. ...         raise SilentAssertionError  
  15. >>> p = PersonClass4()  
  16. >>> t.render(Context({"person": p}))  
  17. "My name is ."  

2,方法調用僅僅在它沒有參數時起做用,不然系統將繼續查找下一個類型(列表索引查詢)
3,顯然一些方法有反作用,讓系統訪問它們是很愚蠢的,並且極可能會形成安全性問
題。
例如你有一個BankAccount對象,該對象有一個delete()方法,模板系統不該該容許作下面的事情
I will now delete this valuable data. {{ account.delete }}
爲了防止這種情況,能夠在方法裏設置一個方法屬性alters_data
若是設置了alters_data=True的話模板系統就不會執行這個方法:
[java]  view plain  copy
 
  1. def delete(self):  
  2.     # Delete the account  
  3. delete.alters_data = True  


不合法的變量怎樣處理
默認狀況下若是變量不存在,模板系統會把它渲染成空string,例如:
[java]  view plain  copy
 
  1. >>> from django.template import Template, Context  
  2. >>> t = Template('Your name is {{ name }}.')  
  3. >>> t.render(Context())  
  4. 'Your name is .'  
  5. >>> t.render(Context({'var': 'hello'}))  
  6. 'Your name is .'  
  7. >>> t.render(Context({'NAME': 'hello'}))  
  8. 'Your name is .'  
  9. >>> t.render(Context({'Name': 'hello'}))  
  10. 'Your name is .'  

系統會靜悄悄地顯示錯誤的頁面,而不是產生一個異常,由於這種狀況一般是人爲的錯誤。
在現實情形下,一個web站點由於一個模板代碼語法的錯誤而變得不可用是不可接受的。
咱們能夠經過設置Django配置更改Django的缺省行爲,第10章擴展模板引擎會咱們會討論這個

玩玩Context對象
大多數狀況下你初始化Context對象會傳遞一個字典給Context()
一旦你初始化了Context,你可使用標準Python字典語法增減Context對象的items:
[java]  view plain  copy
 
  1. >>> from django.template import Context  
  2. >>> c = Context({"foo": "bar"})  
  3. >>> c['foo']  
  4. 'bar'  
  5. >>> del c['foo']  
  6. >>> c['foo']  
  7. ''  
  8. >>> c['newvariable'] = 'hello'  
  9. >>> c['newvariable']  
  10. 'hello'  

Context對象是一個stack,你能夠push()和pop()
若是你pop()的太多的話它將觸發django.template.ContextPopException:
[java]  view plain  copy
 
  1. >>> c = Context()  
  2. >>> c['foo'] = 'first level'  
  3. >>> c.push()  
  4. >>> c['foo'] = 'second level'  
  5. >>> c['foo']  
  6. 'second level'  
  7. >>> c.pop()  
  8. >>> c['foo']  
  9. 'first level'  
  10. >>> c['foo'] = 'overwritten'  
  11. >>> c['foo']  
  12. 'overwritten'  
  13. >>> c.pop()  
  14. Traceback (most recent call last):  
  15. ...  
  16. django.template.ContextPopException  

第10章你會看到使用Context做爲stack自定義模板標籤

模板標籤和過濾器基礎
咱們已經提到模板系統使用內建的標籤和過濾器
這裏咱們看看常見的,附錄6包含了完整的內建標籤和過濾器,你本身熟悉那個列表來了解能夠作什麼是個好主意

if/else
{% if %}標籤計算一個變量值,若是是「true」,即它存在、不爲空而且不是false的boolean值
系統則會顯示{% if %}和{% endif %}間的全部內容:
[java]  view plain  copy
 
  1. {% if today_is_weekend %}  
  2.     <p>Welcome to the weekend!</p>  
  3. {% else %}  
  4.     <p>Get back to work.</p>  
  5. {% endif %}  

{% if %}標籤接受and,or或者not來測試多個變量值或者否認一個給定的變量,例如:
[java]  view plain  copy
 
  1. {% if athlete_list and coach_list %}  
  2.     Both athletes and coaches are available.  
  3. {% endif %}  
  4. {% if not athlete_list %}  
  5.     There are no athletes.  
  6. {% endif %}  
  7. {% if athlete_list or coach_list %}  
  8.     There are some athletes or some coaches.  
  9. {% endif %}  
  10. {% if not athlete_list or coach_list %}  
  11.     There are no athletes or there are some coaches.  
  12. {% endif %}  
  13. {% if athlete_list and not coach_list %}  
  14.     There are some athletes and absolutely no coaches.  
  15. {% endif %}  

{% if %}標籤不容許同一標籤裏同時出現and和or,不然邏輯容易產生歧義,例以下面的標籤是不合法的:
[java]  view plain  copy
 
  1. {% if athlete_list and coach_list or cheerleader_list %}  

若是你想結合and和or來作高級邏輯,只需使用嵌套的{% if %}標籤便可:
[java]  view plain  copy
 
  1. {% if athlete_list %}  
  2.     {% if coach_list or cheerleader_list %}  
  3.         We have athletes, and either coaches or cheerleaders!  
  4.     {% endif %}  
  5. {% endif %}  

屢次使用同一個邏輯符號是合法的:
[java]  view plain  copy
 
  1. {% if athlete_list or coach_list or parent_list or teacher_list %}  

沒有{% elif %}標籤,使用嵌套的{% if %}標籤能夠作到一樣的事情:
[java]  view plain  copy
 
  1. {% if athlete_list %}  
  2.     <p>Here are the athletes: {{ athlete_list }}.</p>  
  3. {% else %}  
  4.     <p>No athletes are available.</p>  
  5.     {% if coach_list %}  
  6.         <p>Here are the coaches: {{ coach_list }}.</p>  
  7.     {% endif %}  
  8. {% endif %}  

確認使用{% endif %}來關閉{% if %}標籤,不然Django觸發TemplateSyntaxError

for
{% for %}標籤容許你按順序遍歷一個序列中的各個元素
Python的for語句語法爲for X in Y,X是用來遍歷Y的變量
每次循環模板系統都會渲染{% for %}和{% endfor %}之間的全部內容
例如,顯示給定athlete_list變量來顯示athlete列表:
[java]  view plain  copy
 
  1. <ul>  
  2. {% for athlete in athlete_list %}  
  3.     <li>{{ athlete.name }}</li>  
  4. {% endfor %}  
  5. </ul>  

在標籤裏添加reversed來反序循環列表:
[java]  view plain  copy
 
  1. {% for athlete in athlete_list reversed %}  
  2. ...  
  3. {% endfor %}  
  4. {% for %}標籤能夠嵌套:  
  5. {% for country in countries %}  
  6.     <h1>{{ country.name }}</h1>  
  7.     <ul>  
  8.     {% for city in country.city_list %}  
  9.         <li>{{ city }}</li>  
  10.     {% endfor %}  
  11.     </ul>  
  12. {% endfor %}  

系統不支持中斷循環,若是你想這樣,你能夠改變你想遍歷的變量來使得變量只包含你想遍歷的值
相似的,系統也不支持continue語句,本章後面的「哲學和限制」會解釋設計的原則
{% for %}標籤內置了一個forloop模板變量,這個變量含有一些屬性能夠提供給你一些關於循環的信息
1,forloop.counter表示循環的次數,它從1開始計數,第一次循環設爲1,例如:
[java]  view plain  copy
 
  1. {% for item in todo_list %}  
  2.     <p>{{ forloop.counter }}: {{ item }}</p>  
  3. {% endfor %}  

2,forloop.counter0相似於forloop.counter,但它是從0開始計數,第一次循環設爲0
3,forloop.revcounter表示循環中剩下的items數量,第一次循環時設爲items總數,最後一次設爲1
4,forloop.revcounter0相似於forloop.revcounter,但它是表示的數量少一個,即最後一次循環時設爲0
5,forloop.first當第一次循環時值爲True,在特別狀況下頗有用:
[java]  view plain  copy
 
  1. {% for object in objects %}  
  2.     {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}  
  3.     {{ object }}  
  4.     </li>  
  5. {% endfor %}  

6,forloop.last當最後一次循環時值爲True
[java]  view plain  copy
 
  1. {% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}  

7,forloop.parentloop在嵌套循環中表示父循環的forloop:
[java]  view plain  copy
 
  1. {% for country in countries %}  
  2.     <table>  
  3.     {% for city in country.city_list %}  
  4.         <tr>  
  5.             <td>Country #{{ forloop.parentloop.counter }} </td>  
  6.             <td>City #{{ forloop.counter }}</td>  
  7.             <td>{{ city }}</td>  
  8.         </tr>  
  9.     {% endfor %}  
  10.     </table>  
  11. {% endfor %}  

富有魔力的forloop變量只能在循環中獲得,當模板解析器到達{% endfor %}時forloop就消失了
若是你的模板context已經包含一個叫forloop的變量,Django會用{% for %}標籤替代它
Django會在for標籤的塊中覆蓋你定義的forloop變量的值
在其餘非循環的地方,你的forloop變量仍然可用
咱們建議模板變量不要使用forloop,若是你須要這樣作來訪問你自定義的forloop,你可使用forloop.parentloop

ifequal/ifnotequal
Django模板系統並非一個嚴格意義上的編程語言,因此它並不容許咱們執行Python語句
(咱們會在‘哲學和限制‘一節詳細討論)。
然而在模板語言裏比較兩個值而且在他們一致的時候顯示一些內容,確實是一個在常見不過的需求了——因此Django提供了ifequal標籤。
{% ifequal %}比較兩個值,若是相等,則顯示{% ifequal %}和{% endifequal %}之間的全部內容:
[java]  view plain  copy
 
  1. {% ifequal user currentuser %}  
  2.     <h1>Welcome!</h1>  
  3. {% endifequal %}  

參數能夠是硬編碼的string,單引號和雙引號都可,下面的代碼是合法的:
[java]  view plain  copy
 
  1. {% ifequal section 'sitenews' %}  
  2.     <h1>Site News</h1>  
  3. {% endifequal %}  
  4. {% ifequal section "community" %}  
  5.     <h1>Community</h1>  
  6. {% endifequal %}  

和{% if %}同樣,{% ifequal %}標籤支持{% else %}
[java]  view plain  copy
 
  1. {% ifequal section 'sitenews' %}  
  2.     <h1>Site News</h1>  
  3. {% else %}  
  4.     <h1>No News Here</h1>  
  5. {% endifequal %}  

其它的模板變量,strings,integers和小數均可以做爲{% ifequal %}的參數:
[java]  view plain  copy
 
  1. {% ifequal variable 1 %}  
  2. {% ifequal variable 1.23 %}  
  3. {% ifequal variable 'foo' %}  
  4. {% ifequal variable "foo" %}  

其它的Python類型,如字典、列表或booleans不能硬編碼在{% ifequal %}裏面,下面是不合法的:
[java]  view plain  copy
 
  1. {% ifequal variable True %}  
  2. {% ifequal variable [1, 2, 3,]%}  
  3. {% ifequal variable {'key': 'value'} %  

若是你須要測試某個變量是true或false,用{% if %}便可

註釋
和HTML或編程語言如Python同樣,Django模板語言容許註釋{# #},如:
[java]  view plain  copy
 
  1. {# This is a comment #}  

模板渲染時註釋不會輸出,一個註釋不能分紅多行
下面的模板渲染時會和模板中的內容同樣,註釋標籤不會解析成註釋
This is a {# comment goes here
and spans another line #}
test.

過濾器
本章前面提到,模板過濾器是變量顯示前轉換它們的值的方式,看起來像下面這樣:
[java]  view plain  copy
 
  1. {{ name|lower }}  

這將顯示經過lower過濾器過濾後{{ name }}變量的值,它將文本轉換成小寫
使用(|)管道來申請一個過濾器
過濾器能夠串成鏈,即一個過濾器的結果能夠傳向下一個
下面是escape文本內容而後把換行轉換成p標籤的習慣用法:
[java]  view plain  copy
 
  1. {{ my_text|escape|linebreaks }}  

有些過濾器須要參數,須要參數的過濾器的樣子:
[java]  view plain  copy
 
  1. {{ bio|truncatewords:"30" }}  

這將顯示bio標量的前30個字,過濾器參數一直使用雙引號
下面是一些最重要的過濾器:
1,addslashed,在任何後斜線,單引號,雙引號前添加一個後斜線
當你把一些文本輸出到一個JavaScript字符串時這會十分有用
2,date,根據一個格式化string參數來格式化date或datetime對象,例如:
[java]  view plain  copy
 
  1. {{ pub_date|date:"F j, Y" }}  

格式化string會在附錄6定義
3,escape,避免給定的string裏出現and符,引號,尖括號
當你處理用戶提交的數據和確認合法的XML和XHTML數據時這將頗有用
escape將做以下的一些轉換:
[java]  view plain  copy
 
  1. Converts & to &amp;amp;  
  2. Converts < to &amp;lt;  
  3. Converts > to &amp;gt;  
  4. Converts "(雙引號) to &amp;quot;  
  5. Converts '(單引號) to &amp;#39;  

4,length,返回值的長度,你能夠在一個list或string上作此操做
或者在任何知道怎樣決定本身的長度的Python對象上作此操做(即有一個__len__()方法的對象)

哲學和限制
如今咱們對於Django地模板系統有了一個感性的認識,下面咱們將指出一些有意爲之的限制和它工做的哲學
不像其餘Web程序組件,程序員對模板系統的意見很是不一致
一個頗有意思的事實:Python至少擁有數十個——若是沒有上百個——的開源模板語言實現,並且看來每個都是由於其創造者認爲現有的模板不能知足他們的要求。
(事實上,聽說寫一個本身的模板系統是已經成了Python開發者必經的儀式了。若是你尚未寫過本身的模板系統,試試看吧,真是頗有意思。)
因此,Django的第一個哲學就是Django不強求你使用它的模板語言
Django的目標是提供一個full-stack框架,提供全部必須的web開發模塊進行高效開發
不少時候,使用Django的模板系統很方便,但不強求你使用它
下面的「在視圖中使用模板」一節咱們會看到在Django中使用另外一套模板語言,它一樣簡單易用
但咱們仍強烈須要Django的模板語言的工做方式,模板系統深植於World Online和Django發明者的
Web開發方式中,下面是其中一些哲學:
1,業務邏輯應該和呈現邏輯分離
模板系統應該只負責控制顯示和顯示相關的邏輯咱們視模板爲一種控制顯示和顯示相關邏輯的工具,僅此而已。模板系統的功能就止於此。
基於這個緣由,Django模板沒法直接調用Python代碼。在Django模板裏,全部的程序設計活動都止於對標籤的使用。
雖然你能夠自定義模板標籤來作任意的事情,但Django本身的模板標籤不容許執行Python代碼。
2,語法應該和HTML/XML解耦
Django的模板系統採用非HTML格式,如普通的文本,有些其它的模板語言是基於XML的
XML的格式容易輸錯,而且XML的模板解析速度也容易變得很慢而難以接受
3,頁面設計者被假定爲熟悉HTML代碼
Django模板系統沒有設計成能夠在Dreamweaver等WYSISYG編輯器中顯示良好
這類編輯器有不少限制,Django但願模板做者直接編輯HTML時感到溫馨
4,頁面設計者被假定爲不是Python程序員
模板系統的做者意識到大部分Web頁面的模板是頁面設計者寫的而不是Python程序員寫的
他們不具有Python知識,但Django也容許模板用Python來寫,它提供了一種直接編寫Python代碼
來擴展模板系統的方法(第10章會介紹更多)
5,目標不是發明一種編程語言
目標只是提供足夠的編程功能,如分支和循環等決定呈現相關的邏輯用
因爲上述的設計哲學,Django模板系統產生以下限制:
1,模板不能設置和改變變量的值
能夠經過自定義模板標籤來達到這個目標(I參看第10章),可是內置Django模板標籤不容許這樣作
2,模板不能調用原生Python代碼
可是也能夠經過自定義標籤來作這件事情

在視圖裏使用模板
咱們已經學習了使用模板系統的基礎,如今咱們來在前一章中的current_datetime視圖中使用它:
[java]  view plain  copy
 
  1. from django.http import HttpResponse  
  2. import datetime  
  3.   
  4. def current_datetime(request):  
  5.     now = datetime.datetime.now()  
  6.     html = "<html><body>It is now %s.</body></html>" % now  
  7.     return HttpResponse(html)  

讓咱們把這個試圖改爲Django模板系統的作法,首先你可能想這樣作:
[java]  view plain  copy
 
  1. from django.template import Template, Context  
  2. from django.http import HttpResponse  
  3. import datetime  
  4.   
  5. def current_datetime(request):  
  6.     now = datetime.datetime.now()  
  7.     t = Template("<html><body>It is now {{ current_date }}.</body></html>")  
  8.     html = t.render(Context({'current_date': now}))  
  9.     return HttpResponse(html)  

這固然用到了模板系統,但它並無解決咱們本章開始介紹的問題,模板仍然嵌在Python代碼裏面
讓咱們經過把模板放在一個單獨的文件裏來解決它,一個簡陋的方式就是把模板保存在文件系統中而後使用Python內建的文件讀取功能獲得模板的內容,下面來看看這樣作的例子:
[java]  view plain  copy
 
  1. from django.template import Template, Context  
  2. from django.http import HttpResponse  
  3. import datetime  
  4.   
  5. def current_datetime(request):  
  6.     now = datetime.datetime.now()  
  7.     # Simple, "dumb" way of saving templates on the filesystem.  
  8.     # This doesn't account for missing files!  
  9.     fp = open('/home/djangouser/templates/mytemplate.html')  
  10.     t = Template(fp.read())  
  11.     fp.close()  
  12.     html = t.render(Context({'current_date': now}))  
  13.     return HttpResponse(html)  

這種方式很是不優雅「
1,它不會處理丟失的文件,若是mytemplate.html不存在或者不可讀,調用open()將觸發IOError異常
2,它硬編碼了你的模板位置,若是你使用這個技術來處理每一個視圖方法,你就會重複複製模板的位置
3,它引入了許多無聊代碼,調用open(),fp.reand()和fp.close()須要不少輸入並且毫無創造性
爲了解決這個問題,咱們將使用模板載入和模板目錄

模板載入
Django提供了方便和強大的API來從硬盤載入模板,從而減小調用模板和模板自己的冗餘
爲了使用Django的模板載入API,首先你須要在settings文件裏告訴Django你把模板放在哪
Django的settings文件時存放你的Django實例的配置的地方,它是一個簡單的具備
模塊級變量的Python模塊,其中每一個設置都是一個變量
當你運行django-admin.py startproject mysite時腳本會爲你建立一個默認的settings文件settings.py
看看這個文件的內容,它包含了像下面這樣的變量:
[java]  view plain  copy
 
  1. DEBUG = True  
  2. TIME_ZONE = 'America/Chicago'  
  3. USE_I18N = True  
  4. ROOT_URLCONF = 'mysite.urls'  

它把本身解釋的很清楚,這些設置和對應的值是簡單的Python變量
因爲settings文件僅僅是一個普通的Python模塊,你能夠在設置新變量前作相似於檢查某個變量的值等動態的事情,這將避免你的settings文件出現Python語法錯誤
這也意味着你應該避免在settings文件裏面出現Python的語法錯誤
後面咱們會深刻講解settings文件,如今先來看看TEMPLATE_DIRS設置,它告訴Django的模板載入機制在哪裏尋找模板
默認狀況下它是一個空的元組,選擇一個你喜歡的存放模板的地方並添加到TEMPLATE_DIRS中去:
[java]  view plain  copy
 
  1. TEMPLATE_DIRS = (  
  2.     '/home/django/mysite/templates',  
  3. )  

須要注意的一些事情:
1,你能夠指定任何目錄,只要那個目錄下的目錄和模板對於你的Web服務器運行時的用戶是可讀的
若是你找不到一個放置模板的位置,咱們推薦你在Django工程目錄下建立一個templates目錄
2,不要忘了模板目錄最後的逗號,Python須要逗號來區分單元素元組和括號括起來的語句
這是新手常常犯的錯誤,若是你想避免這個錯誤,能夠用列表來替代元組,單元素列表不須要結尾的逗號
[java]  view plain  copy
 
  1. TEMPLATE_DIRS = [  
  2.     '/home/django/mysite/templates'  
  3. ]  

元組比列表略微高效,因此咱們推薦使用元組
3,使用絕對路徑很簡單,若是你想更靈活和鬆耦合,你可利用Django的settings文件是簡單的Python代碼
這點來動態構建TEMPLATE_DIRS內容,例如:
[java]  view plain  copy
 
  1. import os.path  
  2.   
  3. TEMPLATE_DIRS = (  
  4.     os.path.join(os.path.dirname(__file__), 'templates'),  
  5. )  

這個例子使用了富有魔力的Python變量__file__,它會被自動設成當前代碼所在的Python模塊的文件名
4,若是你使用Windows,加上硬盤號並使用Unix風格的前斜線而不是後斜線,例如:
[java]  view plain  copy
 
  1. TEMPLATE_DIRS = (  
  2.     'C:/www/django/templates',  
  3. )  

設置好TEMPLATE_DIRS,下一步就是使用Django的模板載入功能而不是硬編碼模板路徑來測試代碼
讓咱們回到current_datetime視圖看看:
[java]  view plain  copy
 
  1. from django.template.loader import get_template  
  2. from django.template import Context  
  3. from django.http import HttpResponse  
  4. import datetime  
  5.   
  6. def current_datetime(request):  
  7.     now = datetime.datetime.now()  
  8.     t = get_template('current_datetime.html')  
  9.     html = t.render(Context({'current_date': now}))  
  10.     return HttpResponse(html)  

這個例子中咱們使用了django.template.loarder.get_template()方法而不是從文件系統手動載入模板
get_template()方法使用模板名做爲參數,算出模板在文件系統的什麼地方,打開它並返回編譯好的Template對象
若是get_template()方法不能找到給定名字的模板,它將觸發TemplateDoesNotExist異常
爲了看看究竟是什麼樣子,啓動Djang server,打開瀏覽器訪問 http://127.0.0.1:8000/now/
假設你的DEBUG設爲True而且你沒有建立current_datetime.html模板,你將看到一個高亮顯示
TemplateDoesNotExist異常的出錯頁面
出錯頁面和第3章那個很相似,但它還有一個「Template-loader postmortem」部分
這個部分告訴你Django嘗試載入哪一個模板以及每一個嘗試失敗了的緣由(如「File does not exist」)
當你嘗試debug模板載入錯誤時這些信息是很是有價值的
如同你能在出錯信息中看到的同樣,Django試圖把TEMPLATE_DIRS中設置的值和傳入get_template()方法的模板名字組合起來查找模板文件。
若是你的TEMPLATE_DIRS中包含'/home/django/templates',最後查找到的文件可能像這樣:'/home/django/templates/current_datetime.html.'
接下來,在你的模板目錄下建立current_datetime.html文件並使用以下的模板代碼:
[java]  view plain  copy
 
  1. <html><body>It is now {{ current_date }}.</body></html>  

刷新瀏覽器頁面你將看到完整渲染的頁面

render_to_response()
Django提供了一個捷徑來使用一行代碼完成載入模板,填充Context,渲染模板,返回HttpResponse對象的工做
這就是render_to_response(),它在django.shortcuts模塊下
大部分狀況下,你都會使用render_to_response()而不是手動完成上述的事情
下面是利用render_to_response()把current_datetime重寫後的例子:
[java]  view plain  copy
 
  1. from django.shortcuts import render_to_response  
  2. import datetime  
  3.   
  4. def current_datetime(request):  
  5.     now = datetime.datetime.now()  
  6.     return render_to_response('current_datetime.html', {'current_date': now})  

多麼不一樣啊!咱們來看看這些代碼:
1,咱們不在import get_template,Template,Context或者HttpResponse
相反,咱們import django.shortcuts.render_to_response,import datetime仍然存在
2,使用current_datetime方法,咱們仍然計算now,但載入模板,建立context,渲染模板和
建立HttpResponse所有被render_to_response()替換,render_to_response返回HttpResponse對象
render_to_response()的第一個參數應該是使用的模板名,對應到模板目錄的相對路徑
第二個參數若是有的話應該是一個用來建立Context的字典
若是你不提供第二個參數,render_to_response()將使用一個空的字典

locals()小技巧
看看最近的current_datetime:
[java]  view plain  copy
 
  1. def current_datetime(request):  
  2.     now = datetime.datetime.now()  
  3.     return render_to_response('current_datetime.html', {'current_date': now})  

這個例子中你會發現你本身計算一些值後存儲在變量中(例如now)並傳遞給模板
懶程序員可能會以爲有點繁瑣,既要給臨時變量取名又要給模板變量取名
這不只僅是冗餘,這是過分輸入
若是你很懶或者你想保持代碼整潔,使用Python內建的locals()方法
locals()返回一個包含當前做用域裏面的全部變量和它們的值的字典,上面的代碼能夠重寫:
[java]  view plain  copy
 
  1. def current_datetime(request):  
  2.     current_date = datetime.datetime.now()  
  3.     return render_to_response('current_datetime.html', locals())  

這裏咱們傳遞locals()的值而不是手動指定context字典,locals()包含了全部定義在當前方法的變量
並且,咱們把now變量重命名爲current_date,由於模板須要的是這個變量名
這個例子中locals()不會給你太大改善,但這個技術能夠幫你少敲鍵盤
使用locals()須要注意的是它包含了全部當前變量,可能包括比你的模板想訪問的更多的變量
上面的例子中,locals()也包括request變量,這依賴於你的程序
最後要注意的是locals()致使了一點點開銷,由於Python不得不動態建立字典
若是你手動指定context字典則能夠避免這項開銷

get_template()的子目錄
將全部的模板都放在同一個目錄下是很笨的方式,你可能想把模板存放模板目錄的子目錄下
這是能夠的,事實上咱們推薦這樣作,而且一些其它高級Django特性,如第9章會提到的generic view系統
也但願這樣的模板結構做爲默認的慣例用法
達到這點很容易,若是你但願訪問子目錄下的模板,只需在模板名前面添加子目錄名和斜線便可:
[java]  view plain  copy
 
  1. t = get_template('dateapp/current_datetime.html')  

由於render_to_response()是對get_template()的小包裝,你能夠在它身上做一樣的事情
對子目錄的深度並無限制,Windows用戶注意使用前斜線而不是後斜線,get_template()使用Unix風格文件名

include模板標籤
咱們已經學習了模板載入機制,咱們要介紹一個利用這個機制的內建標籤:{% include %}
這個標籤容許你引入另外一個模板的內容,標籤的參數是你想引入的模板的名字,名字能夠是變量,
也能夠是單引號或雙引號表示的string
下面兩個例子引入了模板nav.html的內容,這表示單引號和雙引號都是容許的:
[java]  view plain  copy
 
  1. {% include 'nav.html' %}  
  2. {% include "nav.html" %}  

下面的例子引入了includes/nav.html模板:
[java]  view plain  copy
 
  1. {% include 'includes/nav.html' %}  

下面的例子引入了一個名字存在於template_name變量中的模板:
[java]  view plain  copy
 
  1. {% include template_name %}  

和get_template()同樣,請求的模板名前面會加上TEMPLATE_DIRS
若是被引入的模板中包含任何的模板代碼,如標籤和變量等,它將用父模板的context計算它們
若是給定的模板名不存在,Django將作下面兩件事情中的一件:
1,若是DEBUG設置爲True,你將看到一個TemplateDoesNotExist異常的錯誤頁面
2,若是DEBUG設置爲False,標籤將什麼也不顯示

模板繼承
咱們的模板例子如今仍是HTML片段,可是真實世界你將使用Django模板系統輸出完整的HTML頁面
這將致使常見的Web開發問題:怎樣減小一個常見頁面區域的重複和冗餘(如全站導航)?
解決這個問題的經典方式是使用服務器端引入和導向,你能夠在你的HTML裏面嵌套另外一個頁面
Django確實也支持這種方式,上面介紹的{% include %}模板標籤就是這種方案
可是解決這個問題的更好的方式是Django的更優雅的方式模板繼承
本質上來講,模板繼承使你可以構建一個「骨架」模板,裏面包含你的網站的通用部分,而且在裏面
定義子模板能夠覆蓋的「塊」,讓咱們看看前面的例子,編輯current_datetime.html文件:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <title>The current time</title>  
  5. </head>  
  6. <body>  
  7.     <h1>My helpful timestamp site</h1>  
  8.     <p>It is now {{ current_date }}.</p>  
  9.   
  10.     <hr>  
  11.     <p>Thanks for visiting my site.</p>  
  12. </body>  
  13. </html>  

看起來不錯,可是當咱們爲另外一個視圖建立另外一個模板時(如hours_ahead視圖),若是咱們想再建立
一個完整的合法的HTML模板,咱們將建立下面的內容:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <title>Future time</title>  
  5. </head>  
  6. <body>  
  7.     <h1>My helpful timestamp site</h1>  
  8.     <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>  
  9.   
  10.     <hr>  
  11.     <p>Thanks for visiting my site.</p>  
  12. </body>  
  13. </html>  

顯然咱們重複了不少HTML內容,想象一下,若是咱們在每一個頁面都有一些樣式表,導航條,JavaScript...
咱們將會在每一個模板加入重複的HTML內容
這個問題的服務器端解決方案是取出模板中通用的部分而後存放在一個單獨的模板中,而後被每一個模板引入
可能你會把它們存放在一個叫header.html中:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  

可能還需把底下的東西存在一個叫footer.html的文件中:
<hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>
使用基於引入的策略,頭和尾很容易,可是中間的東西就很混亂
例子中,每一個頁面有一個title
[java]  view plain  copy
 
  1. <h1>My helpful timestamp site</h1>  

可是title不能放到hear.html中,由於每一個頁面中的title是不一樣的
Django的模板繼承系統解決了這種問題,你能夠認爲它是服務器引入的「相反」版本
咱們定義不一樣的部分而不是定義相同的部分
第一步是創建基本模板,即你的子模板的框架,下面是一個基本模板的例子:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <title>{% block title %}{% endblock %}</title>  
  5. </head>  
  6. <body>  
  7.     <h1>My helpful timestamp site</h1>  
  8.     {% block content %}{% endblock %}  
  9.     {% block footer %}  
  10.     <hr>  
  11.     <p>Thanks for visiting my site.</p>  
  12.     {% endblock %}  
  13. </body>  
  14. </html>  

咱們把這個模板叫作base.html,它定義了咱們用來在其它頁面使用的基本HTML框架
如今就是子模板覆蓋的工做了,要麼添加內容,要麼不改變塊的內容
(若是你在照着例子作,把base.html保存到模板目錄下)
這裏咱們使用了{% block %}模板標籤,它告訴模板引擎一個子模板可能覆蓋模板的這部份內容
既然咱們有了基本模板,下面咱們來編輯current_datetme.html來使用它:
[java]  view plain  copy
 
  1. {% extends "base.html" %}  
  2.   
  3. {% block title %}The current time{% endblock %}  
  4.   
  5. {% block content %}  
  6. <p>It is now {{ current_date }}.</p>  
  7. {% endblock %}  

同時咱們也建立一個hours_ahead模板來使用基本模板:
[java]  view plain  copy
 
  1. {% extends "base.html" %}  
  2.   
  3. {% block title %}Future time{% endblock %}  
  4.   
  5. {% block content %}  
  6. <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>  
  7. {% endblock %}  

這樣是否是更美觀一些?每一個模板只包含屬於本身的代碼,根本沒有冗餘
若是你想作整個網站的改動,只須要更改base.html便可,其它的模板也會當即響應改動
它是這樣工做的:
1,當你載入模板current_datetime.html,模板引擎發現{% extends %}標籤,注意到這是一個子模板
模板引擎立刻載入父模板base.html
2,模板引擎在base.html裏發現了3個{% block %}標籤,就用子模板的內容替換了這些塊
因而咱們定義的{% block title %}和{% block content %}被使用
注意,既然子模板沒有定義footer塊,那麼模板系統直接使用父模板的值
父模板裏{% block %}標籤的內容一直能夠做爲後援方案
你可使用任意等級的繼承,使用繼承的經常使用方式是按如下三個步驟:
1,建立base.html模板來掌控你的網站的總體外觀,它的內容不多改變
2,爲你的網站建立base_SECTION.html模板,例如,base_photos.html,base_forum.html
這些模板繼承base.html而且包括部分專有的風格和設計
3,爲每一個類別的頁面建立單獨的模板,例如論壇頁面護着照片圖庫頁面
這些模板繼承相應的部分模板
這個方法最大化了代碼重用而且很容易向公用區域添加東西,例如部分專有的導航
下面是一些關於模板繼承的小提示:
1,若是在模板裏使用{% extends %}的話,這個標籤必須在全部模板標籤的最前面,不然模板繼承不工做
2,一般基本模板裏的{% block %}越多越好,子模板沒必要定義全部的父block,鉤子越多越好
3,若是你在不少模板裏複製代碼,極可能你應該把這些代碼移動到父模板裏
4,若是你須要獲得父模板的塊內容,{{ block.super }}變量能夠幫你完成工做
當你須要給父塊添加內容而不是取代它的時候這就頗有用
5,不能在同一模板裏定義多個同名的{% block %},由於塊標籤同時在兩個地方工做,不只僅
在子模板中,並且在父模板中也填充內容,若是子模板有兩個同名的標籤,父模板將不能決定
使用哪一個塊內容來使用
6,你給{% extends %}傳遞的模板名一樣會被get_template()使用,因此會加上TEMPLATE_DIRS設置
7,大部分狀況下,{% extends %}的參數是string,可是也能夠是變量,若是知道運行時才知道
父模板的名字,這能夠幫助你作一些很cool的動態內容

練習
下面是一些鞏固你所學本章知識的練習,這裏咱們介紹了一些新的技巧
1,你有一個音樂家和他們的音樂的列表,它們存儲在一個字典的列表裏,而且硬編碼在你的視圖模塊
(一般咱們使用數據庫來存放這些數據,可是目前咱們還沒講到Django的數據庫層),列表以下:
[java]  view plain  copy
 
  1. MUSICIANS = [  
  2.     {'name': 'Django Reinhardt', 'genre': 'jazz'},  
  3.     {'name': 'Jimi Hendrix',     'genre': 'rock'},  
  4.     {'name': 'Louis Armstrong',  'genre': 'jazz'},  
  5.     {'name': 'Pete Townsend',    'genre': 'rock'},  
  6.     {'name': 'Yanni',            'genre': 'new age'},  
  7.     {'name': 'Ella Fitzgerald',  'genre': 'jazz'},  
  8.     {'name': 'Wesley Willis',    'genre': 'casio'},  
  9.     {'name': 'John Lennon',      'genre': 'rock'},  
  10.     {'name': 'Bono',             'genre': 'rock'},  
  11.     {'name': 'Garth Brooks',     'genre': 'country'},  
  12.     {'name': 'Duke Ellington',   'genre': 'jazz'},  
  13.     {'name': 'William Shatner',  'genre': 'spoken word'},  
  14.     {'name': 'Madonna',          'genre': 'pop'},  
  15. ]  

寫一個Django視圖來顯示HTML的table,列表中的每一個音樂家按順序顯示爲一行
每行有兩列,分別顯示音樂家名字和他的音樂
2,一旦完成上述任務,把table中音樂是jazz或者rock的音樂家的名字樣式設爲粗體
使用style="font-weight: bold;"來修飾td格
3,一旦完成上述任務:給名字爲一個字的音樂家的名字後加上星號
而且在頁面上添加腳註「* Pretentious」前面的粗體字不變
4,下面有3個模板,請你設計繼承關係而且儘量多的去除冗餘
模板1:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <link rel="stylesheet" href="default.css" type="text/css">  
  5.     <title>My to-do list</title>  
  6. </head>  
  7. <body>  
  8.     <h1 id="top">Latest tasks</h1>  
  9.     {% if task_list %}  
  10.         <ul>  
  11.         {% for task in task_list %}<li>{{ task }}</li>{% endfor %}  
  12.         </ul>  
  13.     {% else %}  
  14.         <p>You have no tasks.</p>  
  15.     {% endif %}  
  16.     <hr>  
  17.     <p><a href="#top">Back to top</a>.</p>  
  18. </body>  
  19. </html>  

模板2:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <title>Task: {{ task.title }} | To-do list</title>  
  5.     <link rel="stylesheet" href="default.css" type="text/css">  
  6. </head>  
  7. <body>  
  8.     <h1 id="top">{{ task.title }}</h1>  
  9.     <p>{{ task.description }}</p>  
  10.     <hr>  
  11.     <p><a href="#top">Back to top</a>.</p>  
  12. </body>  
  13. </html>  

模板3:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <title>Completed tasks | To-do list</title>  
  5.     <link rel="stylesheet" href="default.css" type="text/css">  
  6.     <script type="text/javascript" src="completed.js">  
  7. </head>  
  8. <body>  
  9.     <h1 id="top">{{ task.title }}</h1>  
  10.     <p>{{ task.description }}</p>  
  11.     <hr>  
  12.     <p><a href="#top">Back to top</a>.</p>  
  13. </body>  
  14. </html>  


練習答案
1,下面是一個可能的視圖實現:
[java]  view plain  copy
 
  1. from django.shortcuts import render_to_response  
  2.   
  3. MUSICIANS = [  
  4.     # ...  
  5. ]  
  6.   
  7. def musician_list(request):  
  8.     return render_to_response('musician_list.html', {'musicians': MUSICIANS})  

以及模板:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2. <html lang="en">  
  3. <head>  
  4.     <title>Musician list</title>  
  5. </head>  
  6. <body>  
  7.     <table>  
  8.     <tr><th>Musician</th><th>Genre</th></tr>  
  9.     {% for musician in musicians %}  
  10.         <tr>  
  11.         <td>{{ musician.name }}</td>  
  12.         <td>{{ musician.genre }}</td>  
  13.         </tr>  
  14.     {% endfor %}  
  15.     </table>  
  16. </body>  
  17. </html>  

2,笨拙的方式是使用在模板中使用{% ifequal %},視圖和上面的保持不變,模板以下:
[java]  view plain  copy
 
  1. {% for musician in musicians %}  
  2.     <tr>  
  3.     <td {% ifequal musician.genre 'jazz' %}style="font-weight: bold;"{% endifequal %}  
  4.         {% ifequal musician.genre 'rock' %}style="font-weight: bold;"{% endifequal %}>  
  5.       {{ musician.name }}  
  6.     </td>  
  7.     <td>{{ musician.genre }}</td>  
  8.     </tr>  
  9. {% endfor %}  

這顯得很羅嗦並且容易出錯,Django模板系統的關鍵是知道顯示什麼
由於模板沒有完備的編程語言環境的能力,在視圖裏作儘量多的業務邏輯更重要
這樣一來,更清晰的解決問題的方式就是預處理音樂家的名字是否粗體
畢竟這是業務邏輯而不是呈現邏輯,呈現邏輯指出怎樣顯示特殊的類別而不是決定哪些類別是特殊的
這是很重要的區別,下面是視圖的代碼:
[java]  view plain  copy
 
  1. def musician_list(request):  
  2.     musicians = []  
  3.     for m in MUSICIANS:  
  4.         musicians.append({  
  5.             'name': m['name'],  
  6.             'genre': m['genre'],  
  7.             'is_important': m['genre'] in ('rock', 'jazz'),  
  8.         })  
  9.     return render_to_response('musician_list.html', {'musicians': musicians})  

而後這樣使用模板代碼:
[java]  view plain  copy
 
  1. {% for musician in musicians %}  
  2.     <tr>  
  3.     <td{% if musician.is_important %} style="font-weight: bold;"{% endif %}>  
  4.       {{ musician.name }}  
  5.     </td>  
  6.     <td>{{ musician.genre }}</td>  
  7.     </tr>  
  8. {% endfor %}  

看看這個模板是否是更清晰?這比一般狀況更復雜,一般你會和數據庫對象打交道,而數據庫對象
會有自定義方法(如is_important()),下一章咱們會講到數據庫對象
3,同上一題很相似,解決方法也很相似,關鍵是預處理音樂家是否須要在名字後面加星號
這屬於業務邏輯,它屬於視圖,下面是視圖的一種實現:
[java]  view plain  copy
 
  1. def musician_list(request):  
  2.     musicians = []  
  3.     for m in MUSICIANS:  
  4.         musicians.append({  
  5.             'name': m['name'],  
  6.             'genre': m['genre'],  
  7.             'is_important': m['genre'] in ('rock', 'jazz'),  
  8.             'is_pretentious': ' ' not in m['name'],  
  9.         })  
  10.     return render_to_response('musician_list.html', {'musicians': musicians})  

咱們使用' ' not in m['name']表達式,若是m['name']不包含空格就返回True,你也可使用.find()方法:
[java]  view plain  copy
 
  1. 'is_pretentious': m['name'].find(' ') == -1  

注意咱們調用的是is_pretentious而不是has_asterisk,由於使用星號是由呈現層來決定的
咱們使用下面的模板代碼:
[java]  view plain  copy
 
  1. {% for musician in musicians %}  
  2.     <tr>  
  3.     <td{% if musician.is_important %} style="font-weight: bold;"{% endif %}>  
  4.       {{ musician.name }}{% if musician.is_pretentious %}*{% endif %}  
  5.     </td>  
  6.     <td>{{ musician.genre }}</td>  
  7.     </tr>  
  8. {% endfor %}  

別忘了模板底部加上「* Pretentious.」
爲了加分,你應該成爲專家而僅當至少有一個被修飾的音樂家時顯示「* Pretentious」腳註
想下面這樣決定視圖裏是否有被修飾的音樂家
[java]  view plain  copy
 
  1. def musician_list(request):  
  2.     musicians = []  
  3.     has_pretentious = False  
  4.     for m in MUSICIANS:  
  5.         if ' ' not in m['name']:  
  6.             has_pretentious = True  
  7.         musicians.append({  
  8.             'name': m['name'],  
  9.             'genre': m['genre'],  
  10.             'is_important': m['genre'] in ('rock', 'jazz'),  
  11.             'is_pretentious': ' ' not in m['name'],  
  12.         })  
  13.     return render_to_response('musician_list.html', {  
  14.         'musicians': musicians,  
  15.         'has_pretentious': has_pretentious,  
  16.     })  

咱們多傳遞一個模板變量has_pretentious,這樣在模板中使用它:
[java]  view plain  copy
 
  1. {% if has_pretentious %}* Pretentious{% endif %}  

4,這裏是基本模板的一種實現:
[java]  view plain  copy
 
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">  
  2.     <html lang="en">  
  3.     <head>  
  4.         <link rel="stylesheet" href="default.css" type="text/css">  
  5.         <title>{% block title %}{% endblock %}</title>  
  6.         {% block extrahead %}{% endblock %}  
  7.     </head>  
  8.     <body>  
  9.         <h1 id="top">{% block headline %}{% endblock %}</h1>  
  10.         {% block content %}{% endblock %}  
  11.         <hr>  
  12.         <p><a href="#top">Back to top</a>.</p>  
  13.     </body>  
  14.     </html>  

模板1:
[java]  view plain  copy
 
  1. {% extends "base.html" %}  
  2.   
  3. {% block title %}My to-do list{% endblock %}  
  4.   
  5. {% block headline %}Latest tasks{% endblock %}  
  6.   
  7. {% block content %}  
  8. {% if task_list %}  
  9.     <ul>  
  10.     {% for task in task_list %}<li>{{ task }}</li>{% endfor %}  
  11.     </ul>  
  12. {% else %}  
  13.     <p>You have no tasks.</p>  
  14. {% endif %}  
  15. {% endblock %}  

模板2:
[java]  view plain  copy
 
  1. {% extends "base.html" %}  
  2.   
  3. {% block title %}Task: {{ task.title }} | To-do list{% endblock %}  
  4.   
  5. {% block headline %}{{ task.title }}{% endblock %}  
  6.   
  7. {% block content %}<p>{{ task.description }}</p>{% endblock %}  

模板3:
[java]  view plain  copy
 
  1. {% extends "base.html" %}  
  2.   
  3. {% block title %}Completed tasks | To-do list{% endblock %}  
  4.   
  5. {% block extrahead %}<script type="text/javascript" src="completed.js">{% endblock %}  
  6.   
  7. {% block headline %}{{ task.title }}{% endblock %}  
  8.   
  9. {% block content %}<p>{{ task.description }}</p>{% endblock %}  
注意咱們喜歡在幾個{% block %}部分之間放置一個空行,但這只是我的風格 子模板中{% block %}標籤之外的任何內容都不會被渲染   
相關文章
相關標籤/搜索