概述
如果你已經閱讀過上一個章節,那麼你應該已經完成了充分的準備工作並且創建了一個很簡單的具有如下文件結構的Web應用:
親,想要運行這個程序麼?那就運行這個run.py文件,然後在你的瀏覽器裏邊打開http://localhost:5000這個地址.
我們在後面的章節會不斷地從前一章節結束的地方繼續開發我們的應用,所以你要確保你的環境已經正確安裝,並且你的應用能夠正常運行.
爲什麼我們需要使用模板系統
讓我們來考慮一下,我們接下來怎麼樣擴展我們的小程序.
我們要在微博應用裏邊實現一個非常基本的功能,在首頁上面顯示一個歡迎已登錄用戶的標題.暫且忽略當前應用裏邊沒有用戶的狀況,我將會在稍後解決這個問題.
要顯示一個美觀大方的標題,最簡單的方法就是改變我們提供視圖的方式來顯示一些HTML代碼,不如這樣子:\
06 |
user = { 'nickname' : 'Miguel' } |
13 |
+ user['nickname '] + ' ''< / h1> |
現在,在你的瀏覽器裏面刷新一下看看,是不是很爽?
因爲我們這個小程序還支持用戶功能,所以咱用了一個用戶佔位對象,通常它被親切的稱呼爲假數據或測試數據。它可以讓我們關注程序中急需解決的部分。
爽完了,面對實際吧,我希望你會覺得上面這個混合着html代碼的小程序相當噁心。想想看,如果你用一些動態內容生成的一個複雜的HTML頁面,並且想要在由這樣的程序組成的網站中改變一些頁面內容,這將會一件非常蛋疼的事情。
模板系統拯救世界
如果保持業務邏輯和表現的分離,你的網站結構將會組織的更好。不要不信,如果你用python完成了業務代碼,你甚至可以請一個網站設計師幫你完成剩下的部分。模板系統就是幫你實現業務和表現分離的。
讓我們完成第一個模板(fileapp/templates/index.html):
3 |
< title >{{title}} - microblog</ title > |
6 |
< h1 >Hello, {{user.nickname}}!</ h1 > |
如你所見,我們只是寫了一個普通的HTML頁面,唯一跟HTML有區別的就是裏面摻雜了一些以 {{ ... }} 組成的動態內容佔位符。
現在讓我們來看看視圖函數中是如何處理這個模板的(fileapp/views.py):
01 |
from flask import render_template |
07 |
user = { 'nickname' : 'Miguel' } |
08 |
return render_template( "index.html" , |
測試這段程序的重點就是看看模板系統是如何工作的。你可以比較瀏覽器中渲染後的html頁面源碼與模板文件源碼之間的區別。
在上面的程序中,我們從 Flask 框架 import 了一個叫 render_template 的新函數,並用這個函數來渲染模板。並給這個函數賦予了模板文件名和一些變量作爲參數。它將導入的變量替換掉模板中的變量佔位符,並返回渲染後的模板。
讓我們瞭解的更深入點。在 Flask 底層,render_template 函數實際上是調用了 Flask 的一個組件: Jinja2 模板處理引擎。是 Jinjia2 用導入的變量替換掉了模板中對應的 {{ ... }} 代碼塊。
模板中的流程控制
Jinja2 模板系統還支持流程控制語句,我們來嘗試一下在模板中添加一個 if 流程控制語句 (fileapp/templates/index.html):
04 |
< title >{{title}} - microblog</ title > |
06 |
< title >Welcome to microblog</ title > |
10 |
< h1 >Hello, {{user.nickname}}!</ h1 > |
現在我們的模板文件有點智能了。如果我們在視圖函數中忘了定義頁面標題變量 title,它將會使用自已的標題替代。把視圖函數中 render_template 裏的 title 變量取消試試,看看這個 if 流程語句是如何工作的。
模板中使用循環
也許用戶想在他的主頁上展示好友最近寫的文章,有點像人人,或者新浪微博那樣的好友動態,接下來我們就要看看如何來完成這個功能。
首先,創建用戶和文章 (fileapp/views.py):
02 |
user = { 'nickname' : 'Miguel' } |
05 |
'author' : { 'nickname' : 'John' }, |
06 |
'body' : 'Beautiful day in Portland!' |
09 |
'author' : { 'nickname' : 'Susan' }, |
10 |
'body' : 'The Avengers movie was so cool!' |
13 |
return render_template( "index.html" , |
用數組存儲用戶的文章,每一個數組元素都是一個字典,如上代碼所示,這個dict的key是author和body,用來存儲文章的作者和文章內容。當我們決定使用數據庫來存儲這些信息時,這個字典的key可以隱射爲表的一個字段,這裏爲了給大家演示模板的使用,沒有使用數據庫相關的技術,簡單地使用字典和數組模擬用戶和他的好友最近發表的文章。
我們的模板文件現在有了一個新問題。我們剛剛創建了一個包含用戶文章的內容數據,這個數組可能包含任意數量的文章。怎樣才能讓模板根據這個數組的數量自動渲染內容。
要解決這個問題,就需要一個新的流程控制語句:for循環。讓我們來把 for循環添加到模板文件 (fileapp/templates/index.html)
04 |
< title >{{title}} - microblog</ title > |
06 |
< title >microblog</ title > |
10 |
< h1 >Hi, {{user.nickname}}!</ h1 > |
11 |
{% for post in posts %} |
12 |
< p >{{post.author.nickname}} says: < b >{{post.body}}</ b ></ p > |
很簡單吧,你還可以在數組中添加更多的內容看看效果。
模板繼承
接來下,我們需要給這個微博(microblog)添加一個導航菜單,裏面包含 修改個人資料,退出登錄 等類似的鏈接。
直接在 index.html 模板文件中直接加入這個導航條也是可行的,但是當我們有很多模板文件都包含這個導航條時,就會陷入爲了修改導航條的某個地方而不得不在多個文件中往返編輯的尷尬境地。當包含這個導航條的文件越來越多時,你想死的心就會有了。
我們可以使用 Jinja2 的模板繼承功能,它可以使我們把模板中一些公共的內容組合在一起創建一個基礎模板,然後讓其它模板繼承這個基礎模板。
我們先來定義一個基礎模板,裏面包含了導航條和那個最開始寫的關於頁面標題(title)的流程控制語句。(fileapp/templates/base.html):
04 |
< title >{{title}} - microblog</ title > |
06 |
< title >microblog</ title > |
10 |
< div >Microblog: < a href = "/index" >Home</ a ></ div > |
12 |
{% block content %}{% endblock %} |
在這個模板中,我們使用了 block 控制語句來定義繼承模板內容的顯示位置。注意:這個 block 語句中設置的名稱必須唯一。
接下來讓我們修改一下 index.html 模板,讓它繼承於剛剛添加的基礎模板 base.html (fileapp/templates/index.html):
1 |
{ % extends "base.html" % } |
3 |
<h1>Hi, {{user.nickname}}!< / h1> |
5 |
<div><p>{{post.author.nickname}} says: <b>{{post.body}}< / b>< / p>< / div> |
基礎模板 base.html 幫我們搞定了頁面結構和公共內容,所以這個 index.html 模板就成了這幅衰樣了。extends 語句使兩個模板關聯了起來。Jinja2 在渲染 index.html 模板時,發現 extends 語句,就會自動先引入 base.html 基礎模板,並對兩個模板中名爲 content 的 block 語句進行匹配。Jinja2知道如何把兩個模板合併到一起。我們以後創建新的模板時,同樣也會使用這種從基礎模板繼承的方法。
結束語
如果你想節省時間,懶得敲代碼,可以從以下地址下載本章內容的示例代碼:
下載地址:microblog-0.2.zip
提醒各位,這個zip文件中沒有包含 flask 虛似環境的配置,所以你需要自已創建運行環境。搭建運行環境請參考本教程的第一節。
如果你有任何疑問或評論,歡迎在下面留言交流。
下一節我們將會了解一下表單,再會。