Django模板修煉

引言:因爲咱們在使用Django框架時,不會將HTML代碼採用硬編碼的方式,由於會有如下缺點:html

1 1:對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。 站點設計的修改每每比底層 Python代碼的修改要頻繁得多,所以若是能夠在不進行 Python 代碼修改的狀況下變動設計,那將會方便得多。
2 
3 2:Python 代碼編寫和 HTML 設計是兩項不一樣的工做,大多數專業的網站開發環境都將他們分配給不一樣的人員(甚至不一樣部門)來完成。 設計者和HTML/CSS的編碼人員不該該被要求去編輯Python的代碼來完成他們的工做。
4 
5 3:程序員編寫 Python代碼和設計人員製做模板兩項工做同時進行的效率是最高的,遠勝於讓一我的等待另外一我的完成對某個既包含 Python又包含 HTML 的文件的編輯工做。

基於這些緣由,將頁面的設計和Python的代碼分離開會更乾淨簡潔更容易維護。 咱們可使用 Django的模板系統 (Template System)來實現這種模式。python

模板系統的基本知識

1     模板是一個文本,用於分離文檔的表現形式和內容。 模板定義了佔位符以及各類用於規範文檔該如何顯示的各部分基本邏輯(模板標籤)。 模板一般用於產生HTML,可是Django的模板也能產生任何基於文本格式的文檔。

首先從一個使用模板的HTML頁面開始瞭解Django模板:git

 1 1:用兩個大括號括起來的文字(例如 {{ person_name }} )稱爲 變量(variable) 。這意味着在此處插入指定變量的值。 如何指定變量的值呢? 稍後就會說明。
 2 
 3 2:被大括號和百分號包圍的文本(例如 {% if ordered_warranty %} )是 模板標籤(template tag) 。標籤(tag)定義比較明確,即: 僅通知模板系統完成某些工做的標籤。
 4 
 5 3:這個例子中的模板包含一個for標籤( {% for item in item_list %} )和一個if 標籤({% if ordered_warranty %} )
 6  7 4:for標籤相似Python的for語句,可以讓你循環訪問序列裏的每個項目。 if 標籤,正如你所料,是用來執行邏輯判斷的。 在這裏,tag標籤檢查ordered_warranty值是否爲True。若是是,模板系統將顯示{% if ordered_warranty %}和
 8 {% else %}之間的內容;不然將顯示{% else %}和{% endif %}之間的內容。{% else %}是
可選的。 9 10 5:最後,這個模板的第二段中有一個關於filter過濾器的例子,它是一種最便捷的轉換變量輸出格式的方式。如這個例子中的{{ship_date|date:」F j, Y」 }},咱們將變量ship_date傳遞給date過濾器,同時指定參數」F,j,Y」。date過濾器 11 根據參數進行格式輸出。 過濾器是用管道符(|)來調用的,具體能夠參見Unix管道符。

如何使用模板系統

建立模板對象

建立一個 Template 對象最簡單的方法就是直接實例化它。 Template 類就在 django.template 模塊中,構造函數接受一個參數,原始模板代碼。 讓咱們深刻挖掘一下 Python的解釋器看看它是怎麼工做的。

轉到project目錄(在第二章由 django‐admin.py startproject 命令建立), 輸入命令python manage.py shell 啓動交互界面。
一個特殊的Python提示符 若是你曾經使用過Python,你必定好奇,爲何咱們運行python manage.py shell而不是python。這兩個命令都會啓動交互解釋器,可是manage.py shell命令有一個重要的不一樣: 在啓動解釋器以前,它告訴Django使用哪一個設置文件。
Django框架的大部分子系統,包括模板系統,都依賴於配置文件;若是Django不知道使用哪一個配置文件,這些系統將不能工做。
若是你想知道,這裏將向你解釋它背後是如何工做的。 Django搜索DJANGO_SETTINGS_MODULE環境變量,它被設置在settings.py中。例如,假設mysite在你的Python搜索路徑中,那麼DJANGO_SETTINGS_MODULE應該被設置爲:
’mysite.settings’。當你運行命令:python manage.py shell,它將自動幫你處理DJANGO_SETTINGS_MODULE。 在當前的這些示例中,咱們鼓勵你使用`` python manage.py shell``這個方法,這樣能夠免去你大費周章地
去配置那些你不熟悉的環境變量。
隨着你愈來愈熟悉Django,你可能會偏向於廢棄使用`` manage.py shell`` ,而是在你的配置文 件.bash_profile中手動添加 DJANGO_SETTINGS_MODULE這個環境變量。

注:當你建立一個 Template 對象,模板系統在內部編譯這個模板到內部格式,並作優化,作好 渲染的準備。 若是你的模板語法有錯誤,那麼在調用 Template() 時就會拋出 TemplateSyntaxError 異常。程序員

出現TemplateSyntaxError 異常的幾種狀況:

1:無效的tags

2:標籤的參數無效

3:無效的過濾器

4:過濾器的參數無效

5:無效的模板語法

6:未封閉的塊標籤 (針對須要封閉的塊標籤)

模板渲染

  一旦你建立一個 Template 對象,你能夠用 context 來傳遞數據給它。 一個context是一系列變量和它們值的集合。context在Django裏表現爲 Context 類,在 django.template 模塊裏。 她的構造函數帶有一個可選的參數:
一個字典映射變量和它們的值。 調用 Template 對象 的 render() 方法並傳遞context來填充模板:

咱們必須指出的一點是,t.render(c)返回的值是一個Unicode對象,不是普通的Python字符串。 你能夠經過字符串前的u來區分。 在框架中,Django會一直使用Unicode對象而不是普通的字符串。 Django對Unicode支持,將讓你的應用程序輕鬆
地處理各式各樣的字符集,而不只僅是基本的A
-Z英文字符。

字典和Contexts

Python的字典數據類型就是關鍵字和它們值的一個映射。 Context 和字典很相似, Context 還提供更多的功 能。
變量名必須由英文字符開始 (A
-Z或a-z)並能夠包含數字字符、下劃線和小數點。 (小數點在這裏有特別的用途,稍後咱們會講到)變量是大小寫敏感的。

這就是使用Django模板系統的基本規則: 寫模板,建立 Template 對象,建立 Context , 調用 render() 方法。web

同一模板,多個上下文

模板屬於一次產生,屢次使用

Django 模板解析很是快捷。 大部分的解析工做都是在後臺經過對簡短正則表達式一次性調用來完成。 這和基於 XML 的模板引擎造成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且每每比 Django 模板渲染引擎要慢上幾個數量級。正則表達式

深度變量查找

在模板中不只能夠傳遞字符串,還能夠傳遞列表,字典等數據類型。在 Django 模板中遍歷複雜數據結構的關鍵是句點字符 (.)。shell

假設你要向模板傳遞一個 Python 字典。 要經過字典鍵訪問該字典的值,可以使用一個句點:

一樣,也能夠經過句點來訪問對象的屬性。 比方說, Python 的 datetime.date 對象有 year 、 month 和 day幾個屬性,你一樣能夠在模板中使用句點來訪問這些屬性:

自定義一個類,一樣可使用句點號來調用類對象的屬性。

點語法也能夠用來引用對象的* 方法*。

例如,每一個 Python 字符串都有 upper() 和 isdigit() 方法,你在模板中可使用一樣的句點語法來調用它們:

注意:這裏調用方法時並* 沒有* 使用圓括號 並且也沒法給該方法傳遞參數;你只能調用不需參數的方法。django

 

句點也可用於訪問列表索引

注:不容許使用負數列表索引。 像 {{ items.‐1 }} 這樣的模板變量將會引起 TemplateSyntaxError 錯誤。bash

句點號在使用時,對於幾種數據類型調用順序爲:
1:字典類型查找 (好比 foo["bar"] )

2:屬性查找 (好比 foo.bar )

3:方法調用 (好比 foo.bar() )

4:列表類型索引查找 (好比 foo[bar] )
句點查找能夠多級深度嵌套。

例如在下面這個例子中 {{person.name.upper}} 會轉換成字典類型查找(person['name'] ) 而後是方法調用( upper()  ):服務器

方法調用行爲

方法調用比其餘類型的查找略爲複雜一點。 如下是一些注意事項:

在方法查找過程當中,若是某方法拋出一個異常,除非該異常有一個 silent_variable_failure 屬性而且值爲 True ,不然的話它將被傳播。若是異常被傳播,模板裏的指定變量會被置爲空字符串。

若是要防止模板載入時,避免不想觸發函數的執行,能夠給方法設置一個特殊的屬性。

模板系統不會執行任何以該方式進行標記的方法。 接上面的例子,若是模板文件裏包含了{{ account.delete }} ,對象又具備 delete()方法,並且delete() 有alters_data=True這個屬性,那麼在模板載入時, delete()方法將不會被執行。 它將靜靜地錯誤退出。

如何處理無效數據

默認狀況下,若是一個變量不存在,模板系統會把它展現爲空字符串,不作任何事情來表示失敗。

系統靜悄悄地表示失敗,而不是引起一個異常,由於這一般是人爲錯誤形成的。 這種狀況下,由於變量名有錯誤的情況或名稱, 全部的查詢都會失敗。 現實世界中,對於一個web站點來講,若是僅僅由於一個小的模板語法錯誤而形成沒法訪問,這是不可接受的。

 

瞭解上下文(context)對象

 

 

基本的模板標籤和過濾器

標籤

if/else

 

{% if %} 標籤檢查(evaluate)一個變量,若是這個變量爲真(即,變量存在,非空,不是布爾值假),系統會顯示在 {% if %} 和 {% endif %} 之間的任何內容,例如:
{% if 條件 %}
     執行操做
{% endif %}
{% else %} 標籤是可選的:
{% if 條件%}
     執行操做1
{% else %}
     執行操做2
{% endif %}
拓展:
在Python和Django模板系統中,如下這些對象至關於布爾值的False:

1:空列表([])

2:空元組(())

3:空字典({})

4:空字符串('')

5:零值(0)

6:特殊對象None

7:對象False

8:提示:你也能夠在自定義的對象裏定義他們的布爾值屬性(這個是python的高級用法)。

除以上幾點之外的全部東西都視爲True。
{% if %} 標籤接受 and , or 或者 not 關鍵字來對多個變量作判斷 ,或者對變量取反( not ),例如:
{% if 條件1 and 條件2 %}
     執行操做1
{% endif %}

{% if not 條件1 %}
     執行操做2
{% endif %}

{% if 條件1 or 條件2 %}
     執行操做1
{% endif %}

{% if not 條件1 or 條件2 %}
     執行操做1
{% endif %}

{% if 條件1 and not 條件2 %}
     執行操做1
{% endif %}
 {% if %} 標籤不容許在同一個標籤中同時使用 and 和 or ,由於邏輯上可能模糊的,例如,以下示例是錯誤的: 好比這樣的代碼是不合法的:
{% if 條件1 and 條件2 or 條件3 %} #這是錯誤示例!!!
系統不支持用圓括號來組合比較操做。 若是你確實須要用到圓括號來組合表達你的邏輯式,考慮將它移到模板以外處理,而後以模板變量的形式傳入結果吧。 或者,僅僅用嵌套的{% if %}標籤替換吧,就像這樣:
{% if 條件1 %}
     {% if 條件2 or 條件3 %}
         執行操做
     {% endif %}
{% endif %}
屢次使用同一個邏輯操做符是沒有問題的,可是咱們不能把不一樣的操做符組合起來。 例如,這是合法的:
{% if 條件1 or 條件2 or 條件3 or 條件4 %} #這是容許的寫法!
並無 {% elif %} 標籤, 請使用嵌套的`` {% if %}`` 標籤來達成一樣的效果:
{% if 條件1 %}
     執行操做1
{% else %}
      執行操做2
     {% if 條件2 %}
         執行操做3
     {% endif %}
{% endif %}

注:必定要用 {% endif %} 關閉每個 {% if %} 標籤。

for

{% for %} 容許咱們在一個序列上迭代。 與Python的 for 語句的情形相似,循環語法是 for X in Y ,Y是要迭代的序列而X是在每個特定的循環中使用的變量名稱。 每一次循環中,模板系統會渲染在 {% for %} 和{% endfor %} 之間的全部內容。
<ul>
    {% for 元素 in 可迭代對象 %}
     <li>{{ 元素.屬性}}</li>
    {% endfor %}
</ul>
給標籤增長一個 reversed 使得該列表被反向迭代:
{% for 元素 in 可迭代對象 reversed %}
    ....(執行操做)
{% endfor %}
能夠嵌套使用 {% for %} 標籤(相似於python中的循環嵌套):
{% for 對象1 in 可迭代對象 %}
     <h1>{{ 對象1.屬性}}</h1>
     <ul>
         {% for 對象2 in 對象1.屬性(集合) %}
         <li>{{ 對象2}}</li>
         {% endfor %}
     </ul>
{% endfor %}
在執行循環以前先檢測列表的大小是一個一般的作法,當列表爲空時輸出一些特別的提示。
{% if 條件1 %}
     {% for 對象 in 可迭代對象 %}
         <p>{{ 對象 .屬性}}</p>
     {% endfor %}
{% else %}
         執行操做
{% endif %}
由於這種作法十分常見,因此`` for`` 標籤支持一個可選的`` {% empty %}`` 分句,經過它咱們能夠定義當列表爲空時的輸出內容 下面的例子與以前那個等價:
{% for 對象 in 可迭代對象 %}
     執行操做1
{% empty %}
     執行操做2
{% endfor %}

注:Django不支持退出循環操做。 若是咱們想退出循環,能夠改變正在迭代的變量,讓其僅僅包含須要迭代的項目。 同理,Django也不支持continue語句,咱們沒法讓當前迭代操做跳回到循環頭部。

 

在每一個`` {% for %}``循環裏有一個稱爲`` forloop`` 的模板變量。這個變量有一些提示循環進度信息的屬性。
forloop.counter 老是一個表示當前循環的執行次數的整數計數器。 這個計數器是從1開始的,因此在第一次循環時 forloop.counter 將會被設置爲1。
{% for item in demo_list %}
     <p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}
forloop.counter0 相似於 forloop.counter ,可是它是從0計數的。 第一次執行循環時這個變量會被設置爲0。
forloop.revcounter 是表示循環中剩餘項的整型變量。 在循環初次執行時 forloop.revcounter 將被設置爲序列中項的總數。 最後一次循環執行中,這個變量將被置1。
forloop.revcounter0 相似於 forloop.revcounter ,但它以0作爲結束索引。 在第一次執行循環時,該變量會被置爲序列的項的個數減1。
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 %}    

執行結果:Link1 | Link2 | Link3 | Link4
forloop.parentloop 是一個指向當前循環的上一級循環的 forloop 對象的引用(在嵌套循環的狀況下)。例子在此:
{% 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 %}    

注:forloop 變量僅僅可以在循環中使用。 在模板解析器碰到{% endfor %}標籤後,forloop就不可訪問了。

ifequal|ifnotequal

{% ifequal %} 標籤比較兩個值,當他們相等時,顯示在 {% ifequal %} 和 {% endifequal %} 之中全部的值。
1 {% ifequal section 'sitenews' %}
2      <h1>Site News</h1>
3 {% endifequal %}
4 
5 {% ifequal section "community" %}
6      <h1>Community</h1>
7 {% endifequal %}
和 {% if %} 相似, {% ifequal %} 支持可選的 {% else%} 標籤:
1 {% ifequal section 'sitenews' %}
2      <h1>Site News</h1>
3 {% else %}
4      <h1>No News Here</h1>
5 {% endifequal %}
只有模板變量,字符串,整數和小數能夠做爲 {% ifequal %} 標籤的參數。下面是合法參數的例子:
{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}
其餘任何類型,例如Python的字典類型、列表類型、布爾類型,不能用在 {% ifequal %} 中。
1 {% ifequal variable True %}
2 {% ifequal variable [1, 2, 3] %}
3 {% ifequal variable {'key': 'value'} %}

註釋

1 #單行註釋
2 {# This is a comment #}
3 
4 #多行註釋
5 {% comment %}
6 This is a
7 multi‐line comment.
8 {% endcomment %}

過濾器

模板過濾器是在變量被顯示前修改它的值的一個簡單方法。 過濾器使用管道字符。例如:
{{ name|lower }}
過濾管道能夠被* 套接* ,既是說,一個過濾器管道的輸出又能夠做爲下一個管道的輸入,如此下去。 下面的例子實現查找列表的第一個元素並將其轉化爲大寫。
1 {{ my_list|first|upper }}
有些過濾器有參數。 過濾器的參數跟隨冒號以後而且老是以雙引號包含。 例如:
1 {{ bio|truncatewords:"30" }}

還有以下一些過濾器:

addslashes : 添加反斜槓到任何反斜槓、單引號或者雙引號前面。 這在處理包含JavaScript的文本時是很是
有用的。

date : 按指定的格式字符串參數格式化 date 或者 datetime 對象, 範例:{{ pub_date|date:"F j, Y" }}

length : 返回變量的長度。 對於列表,這個參數將返回列表元素的個數。 對於字符串,這個參數將返回字
符串中字符的個數。

 視圖中使用模板

示例:

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})

locals() 技巧

若是你是個喜歡偷懶的程序員並想讓代碼看起來更加簡明,能夠利用 Python 的內建函數 l字典對全部局部變量的名稱與值進行映射。 所以,前面的視圖能夠重寫成下面這個樣子:

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

注:view視圖函數中的局部變量必定要和想要渲染的模板名稱一致!!!

include 模板標籤

 {% include %} 。該標籤容許在(模板中)包含其它的模板的內容。 標籤的參數是所要包含的模板名稱,能夠是一個變量,也能夠是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減小重複。

 模板繼承

  到目前爲止,咱們的模板範例都只是些零星的 HTML 片斷,但在實際應用中,你將用 Django 模板系統來建立整個 HTML 頁面。 這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減小共用頁面區域(好比站點導航)所引發的重複和冗餘代碼?解決該問題的傳統作法是使用 服務器端的 includes ,你能夠在 HTML 頁面中使用該指令將一個網頁嵌入到另外一箇中。 事實上, Django 經過剛纔講述的 {% include %} 支持了這種方法。 可是用 Django 解決此類問題的首選方法是使用更加優雅的策略—— 模板繼承 。本質上來講,模板繼承就是先構造一個基礎框架模板,然後在其子模板中對它所包含站點公用部分和定義塊進行重載。

模板繼承實現步驟:

  第一步是定義 基礎模板 , 該框架以後將由 子模板 所繼承。 如下是咱們目前所講述範例的基礎模板:

<!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>

這個叫作 base.html 的模板定義了一個簡單的 HTML 框架文檔,咱們將在本站點的全部頁面中使用。 子模板的做用就是重載、添加或保留那些塊的內容。咱們使用一個之前已經見過的模板標籤: {% block %} 。 全部的 {% block %} 標籤告訴模板引擎,子模板能夠重載這些部分。 每一個{% block %}標籤所要作的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。

  第二步:在須要繼承模板頁面首行使用extends引入模板頁面,而後使用指定內容替換模板的可變板塊。例如:

{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
工做方式。 在加載 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 %} 對所傳入模板名稱使用的加載方法會將模板名稱被添加到 TEMPLATE_DIRS 設置以後。
  多數狀況下, {% extends %} 的參數應該是字符串,可是若是直到運行時方能肯定父模板名,這個參數也
  能夠是個變量。 這使得你可以實現一些很酷的動態功能
相關文章
相關標籤/搜索