電腦做什麼事 第十八章 利用樣版系統編排





































電腦做什麼事 第十八章 利用樣版系統編排






上一章中建立了留言板的應用程式,接著進入後台進行管理,可是還沒有提及如何做出供他人瀏覽的網頁。我們的資料越來越多,該如何做出網頁呢?



第十六章我們做網頁的方法是直接把HTML寫進字串中,然後利用網址改變以及抓取電腦的現在時間,直接以變數提供給HTML的字串,最後作為HttpRespons()的參數,使之輸出到瀏覽器中。


這是利用Django做網頁的方法之一,當要放進網頁的內容越來越多,同時也希望進行更多的版面配置,使網頁呈現出美觀的外在,把HTML寫進字串的方法就顯得冗贅繁複。其實,這並不是Django做網頁唯一途徑,普遍的作法是利用樣版系統編排網頁的版面。


樣版系統結合HTML語法,輔以標籤過濾器,結合程式語言的部份功能,在Django中作為描述網頁編排的標記語言。這使Django相較其他環繞MVC原則的網頁框架有了一個明顯的特色,也就是Django除了遵守MVC原則外,另具備MTV的概念。


MTV的概念






MTV為Model-Template-View的頭字母縮寫詞,M與MVC原則中的M的意義一樣,V稍有差異,MTV的V為控制網頁顯示的方法。T為Template的第一個字母,中文意思就是樣版,樣版系統正是我們這一章的主題。


MTV可以直接與專案內的檔案連起來思考,M如同留言板應用程式中的models.py,V為views.py。至於T,因為一個樣版就是單一個檔案,專案中的樣版數沒有限制,這是說網頁的顯示依需要可以套用不同的樣版,實際上我們會建立一個名為「template」的資料夾放置樣版的檔案。


為什麼前面要介紹MVC,這邊要介紹MTV呢?兩者的內涵似乎重疊了許多?因為MVC是一般所倡導的概念,然而就Django而言,MVC的V是MTV中的T,MVC的C則是MTV中的V,兩者的概念說到底是一樣的。


原因不外由於MVC是很常用的詞,而MTV為針對解釋Django之情形所用的詞,MVC在Django之中容易誤解,尤其在Controller的部份,廣義的說整個Django都算是屬於Controller的部份,然而MTV就不會了,因為MTV各有專屬的檔案或資料夾。


我們製作樣版檔案會用到Django的樣版語言,這當中也需要一些HTML,這似乎是要進入另一個與Python完全不同的主體嗎?不是的,樣版系統作為網頁顯示的元件,其實這也是基於Django的設計哲學,「寬鬆的結合」,MTV每個元件各自分開獨立運作。


接著繼續下去我們會發現樣版語言跟Python頗為相似,不過還是得留心不同的地方。


now.html及page.html






我們延續上一章的demo專案,在demo資料夾內建立一個template資料夾。
建立template資料夾


然後我們先以第十六章「現在時間是 %s。」及「您來到了第 %s 頁;」的例子,簡介樣版系統的語法。樣版的檔案實際上就是HTML檔案,我們先在template資料夾中建立now.html,作為顯示現在時間的樣版,裡頭放入以下的內容。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

<html>
<head>
<title>顯示現在時間</title>
</head>

<body>
<h1>現在時間是 {{ current_time }} 。</h1>

你好啊!
右邊會隨機出現一個0到9之間的整數: {{ number|random }}
</body>
</html>



其中,兩個大括弧中間包著的current_time,
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{{ current_time }}



這在樣版中表示current_time為一個變數。而
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{{ number|random }}



number也是變數,其後的「|」為管線命令符號,經過random參數的隨機透析,使number變數中的數值以過濾後的方式呈現,這就是上述的過濾器。稍後,我們會抓取電腦現在時間儲存到current_time內,另外會指定一組數字給number。


另外在template資料夾中建立page.html,作為顯示頁數的樣版,內容如下。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

<html>
<head>
<title>顯示頁數及現在時間</title>
</head>

<body>
<h1>現在時間是 {{ current_time }} 。</h1>
{% ifequal offset "99" %}
<p>這是最後一頁囉!</p>
{% else %}
<p>您來到了第 {{ offset }} 頁。</p>
{% endifequal %}
</body>
</html>



變數是用兩個大括弧圍起來,而這裡
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% ifequal offset "99" %}




<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% endifequal %}



之間被稱為區塊,大括弧接百分比符號中間的{% ifequal %}則是標籤,標籤類似Python中的關鍵字,用於指揮程式進行的過程。這裡的{% ifequal %}後面接兩個參數,第一個為變數,第二個為數值,判斷其後所接的變數是否與數值相等,若是相等則在網頁中顯示區塊內的
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

<p>這是最後一頁囉!</p>



若是不相等則在網頁中顯示「else」標籤中的
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

<p>您來到了第 {{ offset }} 頁。</p>



記不記得在第十六章,這部份是寫在view.py的page_counter()函數之中,因為那時候網頁顯示的工作一併交給了view.py的page_counter()函數,現在的分開處理,正是Django設計哲學中「寬鬆的結合」最佳的例證。


有一點需要留意的,樣版語言的標籤與HTML類似,兩者都是成對出現的,如上例{% ifequal %}的標籤,標籤有效範圍終止的地方是用{% endifequal %}標籤,這也就代表了區塊的結束。


樣版目錄的設定






我們還需要做幾個設定調整才能夠讓網頁顯示,首先開啟setting.py,找到如下的部份。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or
"C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)



這些註解化的文字大略是說需要用字串的格式,將樣版目錄的路徑放置在這裡。我們簡單一點,將這部份的程式碼更改如下。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

TEMPLATE_DIRS = (
'.',
)



我們所加入的「'.'」,好比是將尋找樣版資料夾的根目錄設定為與專案相同。接著將guestbook資料夾中的views.py更改如下。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

from django.shortcuts import render_to_response
import datetime

def current_datetime(request):
cdt = datetime.datetime.now()
now_template = r"templatenow.html"
now_context = {'current_time':cdt, 'number':range(10)}
return render_to_response(now_template, now_context)

def page_counter(request, offset):
cdt = datetime.datetime.now()
page_template = r"templatepage.html"
page_context = {'current_time':cdt, 'offset':offset}
return render_to_response(page_template, page_context)



這裡比較特別的是從django的shortcuts模組引入render_to_response()函數,需要兩個參數,第一個參數為指定樣版的路徑,儲存在字串之中,第二個參數則是樣版中變數與變數所儲存的數值,利用配對型態的字典來儲存,key為變數名稱,value則為變數內容,要留意需要用字串型態儲存用作變數名稱的key。


render_to_response()函數是一種捷徑函數,因為利用樣版顯示網頁,實際上需要先把樣版檔案傳換成Template物件,而變數名稱及內容則要轉換為Context物件,捷徑函數便利我們省卻了許多麻煩。


最後還須修改一下urls.py,我們替urlpatterns加入兩行的網址設定。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^time/$', 'demo.guestbook.views.current_datetime'),
(r'^time/(d{1,2})/$', 'demo.guestbook.views.page_counter'),
)



現在可以來看看結果了,別忘記要啟動伺服器,然後連結到http://127.0.0.1:8000/time/。
顯示現在時間的頁面


再連結到其中的一頁看看吧!
第33頁


其他的標籤及過濾器






樣版系統除了支援HTML的註解標籤外,本身也有用作註解的標籤。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{# 請將註解放在這裡 #}



大括弧加井字號,這就是Django樣版語言所用的註解。除了註解之外,Python中用於條件判斷的if陳述,樣版語言也有相對應的標籤。


and、or、not也都支援可用,供比較測試多個變數。別忘了{% if %}標籤要以{% endif %}結尾。當然我們已經見過的{% else %}標籤也有支援,然而elif陳述沒有支援,所以當選擇判斷的條件多於兩個時,就要用巢狀的方式。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% if a and b %}
變數a與b都為真的話,就會出現這些內容………
{% else %}
{% if c %}
變數a與b都不為真,但是c為真,就會出現這些內容………
{% else %}
變數a、b、c都不為真,就會出現這些內容………
{% endif %}
{% endif %}



就跟HTML標籤同理,任一個「if」標籤都要以一個「endif」結尾,不然Django會不知道到哪裡結束「if」,從而導致發生錯誤。


另外也有{% for %}標籤。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{# 這會依序顯示data中的資料… #}
{% for i in data %}
{{ i }}<BR />
{% endfor %}



這會一列一列的在網頁中顯示變數data所儲存的個別數值。


過濾器是針對變數運用的,我們多舉幾個例子說明,譬如變數one中儲存整數1
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

1+1={{ one|add:”1” }}



add會替變數加上其後的參數,於是顯示的結果會是「1+1=2」。又如果變數name存放英文姓名
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{{ name|capfirst }}



不論name中字母的大小寫,capfirst都會將第一個字母設定為大寫。我們上面的變數current_time,也可以利用過濾器提出個別的部份
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{{ current_time|date:”M” }}



date用大寫的「M」作為參數,網頁上只會顯示月份。當然,過濾器還有很多,我們再舉一個例子,如變數pi中所儲存的是圓周率3.14159……………..
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{{ pi|floatformat:3 }}



floatformat會調整pi在小數點後顯示的位數,這裡給了「3」作為參數,就是會在網頁中顯示圓周率為「3.141」。


Django樣版系統的標籤及過濾器還有很多,可以參考Built-in template tags and filters。



樣版的繼承






不論在now.html或是page.html中,我們都重複寫了<html></html>、<head></head>>及<body></body>等HTML標籤,另外也重複寫了「<h1>現在時間是 {{ current_time }} 。</h1>>」這一行。雖然目前例子很簡單,但是當網頁的內容頁數與日俱增,而每一頁又有許多相同的元件時,一再的重複,就顯得冗長累贅。


Python語言的繼承,讓某一型態可以輕易的獲取原先定義型態的屬性與方法,進而覆寫或是增加新的屬性。Djnago的樣版語言沿襲Python語言繼承的優點,我們可以把網頁的共同元件寫進一個基礎樣版中,不同類型的網頁可以繼承基礎樣版的內容。如下我們以base.html為基礎樣版。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

<html>
<head>
<title>{% block title %} 基礎樣版 {% endblock %}</title>
</head>

<body>
<h1>現在時間是 {{ current_time }} 。</h1>

{% block content %}{% endblock %}
</body>
</html>



標籤「block」通常會出現在某特定區域,如<title></title>這對HTML標籤之內,其後也需要接一個專屬的名稱,作為識別位置之用,然後在被繼承的樣版中,相同的「block」標籤名稱即可覆寫基礎樣版的內容。


我們另外寫now2.html與page2.html來繼承base.html,now2.html如下。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% extends "template/base.html" %}

{% block title %}
顯示現在時間
{% endblock %}

{% block content %}
你好啊!
右邊會隨機出現一個0到9之間的整數: {{ number|random }}
{% endblock %}



凡是需要繼承基礎樣版的樣版,當案開頭都要用標籤「extends」,其後接一個字串內含基礎樣版的路徑,然後將所欲覆寫的標籤內容補上,如此一來繼承工作就可順利進行。


省掉了重複撰寫的東西,是否單純了許多呢?page2.html如下。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% extends "template/base.html" %}

{% block title %}
顯示現在時間及頁數 {{ offset }}
{% endblock %}

{% block content %}
{% ifequal offset "99" %}
<p>這是最後一頁囉!</p>
{% else %}
<p>您來到了第 {{ offset }} 頁。</p>
{% endifequal %}
{% endblock %}



如須看網頁的結果,我們還須回到views.py修改樣版的路徑字串,也就是now_template與page_template兩個區域變數。這會跟稍早的結果一樣,請自行嘗試看看,別忘了要在伺服器啟動狀態下連結網頁。


留言板的索引頁 - index.html






我們接下來利用樣版系統做出上一章留言板的網頁瀏覽介面,同樣繼承自base.html,這樣網頁都會出現「現在時間」。總共需要兩個樣版,一個是所有留言的目錄,另一個是個別留言的瀏覽頁。


關於所有留言目錄的樣版index.html,如下。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% extends "template/base.html" %}

{% block title %}
簡單的留言板
{% endblock %}

{% block content %}
{% if entry_list %}
{% for entry in entry_list %}
<p><a href="/{{ entry.id }}">{{ entry.title }}</a></p>
{% endfor %}
{% else %}
<p>尚無留言 = _ =</p>
{% endif %}
{% endblock %}



網頁標題設為「簡單的留言板」,然後在{% block content %}裡頭,我們用了{% if %}標籤,檢查是否有留言,若是沒有,網頁顯示「尚無留言」,若是有,便利用{% for %}標籤依留言的id屬性,將留言一個標題以一個段落顯示到網頁上。


id屬性繼承自Model物件,由留言的順序從1開始給予整數的序數,這也作為閱覽單獨留言的網址,這個網址「href="/{{ entry.id }}"」,如第一個留言是指http://127.0.0.1:8000/1/的位置。


接著在views.py加入index()函數,如下。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

def index(request):
cdt = datetime.datetime.now()
index_template = r'template/index.html'
index_context = {'current_time':cdt, 'entry_list': Entry.objects.all()}
return render_to_response(index_template, index_context)



Entry.objects.all()會取得所有已經建立的Entry物件,當然,我們也要先引入Entry物件。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

from demo.guestbook.models import Entry



我們預備把http://127.0.0.1:8000/的位置給索引頁,因此urls.py的urlpatterns修改如下。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^time/$', 'demo.guestbook.views.current_datetime'),
(r'^time/(d{1,2})/$', 'demo.guestbook.views.page_counter'),
(r'^$', 'demo.guestbook.views.index'),
)



我們連結到http://127.0.0.1:8000/看看吧!
首頁 - 留言板的目錄


瀏覽個別留言 - entry.html






個別留言的樣版entry.html如下。
<!--《電腦做什麼事》的範例程式碼 
http://pydoing.blogspot.com/ -->

{% extends "template/base.html" %}

{% block title %}
{{ entry.title }}
{% endblock %}

{% block content %}
<h1>留言標題: {{ entry.title }}</h1>
<h1>留 言 者: {{ entry.name }}</h1>
<h1>網    址: {{ entry.url}}</h1>
<p>{{ entry.text }}</p>
<p><a href="/">回到目錄</a></p>
{% endblock %}



網頁標題亦即Entry物件的title屬性,底下依序用<h1>標籤顯示留言標題、留言者、網址,留言本文與的「回到目錄」連結則是用<p>段落標籤,「回到目錄」也就是回到索引頁,所顯示的四個項目分別是Entry物件的四項屬性。


我們在views.py加入entry()函數處理個別留言的網頁顯示。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

def entry(request, entry_id):
cdt = datetime.datetime.now()
message = get_object_or_404(Entry, pk=entry_id)
entry_template = r'template/entry.html'
entry_context = {'current_time':cdt, 'entry': message}
return render_to_response(entry_template, entry_context)



這裡用了另一個捷徑函數get_object_or_404(),其能夠取得物件所有的屬性,所以要多引入get_object_or_404的名稱修改如下。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

from django.shortcuts import render_to_response, get_object_or_404



連結網址設定urls.py的urlpatterns加入這一行。
#《電腦做什麼事》的範例程式碼 
#http://pydoing.blogspot.com/

(r'^(?P<entry_id>d+)/$', 'demo.guestbook.views.entry'),



注意,「(?P<entry_id>d+)」仍是正規表示法,這可以把連結網址設定為id屬性值。


我們來看看結果吧!先點擊第二筆留言「Django真好玩」。
第二筆留言


結果如上,我們再點擊「回到目錄」,然後回去看第一筆留言。
第一筆留言


沒錯,並無出現網址,網頁同時也正常顯示。


※ 本文同時登載於 OSSF 網站的下載中心 - PDF ※ 回 - 目錄 ※




















window.___gcfg = { 'lang': 'zh-TW' };





Popular posts from this blog

Mái uốn xoăn như “bà thím” vẫn được loạt sao Hàn yêu thích, nhờ sự thay đổi này mà ai cũng trẻ trung hơn hẳn

Cô bé "The Voice Kids Anh" nhận ngay nút vàng tại "Got Talent Mỹ" với màn trình diễn quá đáng yêu

qBittorrent 4.1.1 免安裝中文版 - 取代uTorrent的BT下載器