一、概述
在第二部分中,我將討論如何使用模板。
學習完第一章之後,你已經擁有了一個雖然簡單,但是可以成功運行Web應用,它的文件結構如下:
microblog
venv
app
__init__.py
routes.py
microblog.py在終端會話中設置環境變量FLASK_APP=microblog.py,然後執行flask run命令來運行應用。 包含這個應用的Web服務啟動之後,你可以通過在Web瀏覽器的地址欄中鍵入URL http://localhost:5000/ 來驗證。
本章將沿用這個應用,在此之上,你將學習如何生成包含複雜結構和諸多動態組件的網頁。如果對這個應用和相關開發流程有所遺忘,請回顧第一章。
二、什麼是模板?
我設計的微博應用程序的主頁會有一個歡迎用戶的標題。雖然目前的應用程序還沒有實現用戶概念,但這不妨礙我使用一個Python字典來模擬一個用戶,如下所示:
user = {'username':'Miguel'}創建模擬對象是一項實用的技術,它可以讓你專註於應用程序的一部分,而無需為系統中尚不存在的其他部分分心。 在設計應用程序主頁的時候,我可不希望因為沒有一個用戶系統來分散我的注意力,因此我使用了模擬用戶對象,來繼續接下來的工作。
原先的視圖函數返回簡單的字符串,我現在要將其擴展為包含完整HTML頁面元素的字符串,如下所示:
from app import app
@app.route('/')
@app.route('/index')
def index():
user = {'username': 'Miguel'}
return '''
<html>
<head>
<title>Home Page - Microblog</title>
</head>
<body>
<h1>Hello, ''' + user['username'] + '''!</h1>
</body>
</html>運行flask run 更新視圖功能,並查看應用程序在瀏覽器中的顯示效果。

利用上述的代碼更新這個視圖函數,然後再次在瀏覽器打開它的URL看看結果。
如果我說這個函數返回HTML的方式並不友好的話,你可能會覺得詫異。設想一下,當這個視圖函數中的用戶和博客不斷變化時,裡面的代碼將會變得多麼的複雜。應用的視圖函數及其關聯的URL也會持續增長。如果哪天我決定更改這個應用的布局,那就不得不更新每個視圖函數的HTML字符串。顯然,隨着應用的擴張,這種方式完全不可行。
將應用程序的後台邏輯和網頁布局劃分開來,你不覺得更容易組織管理嗎?甚至你可以聘請一位Web設計師來設計一個殺手級的網站前端,而你只需要用Python編寫後台應用邏輯。
模板有助於實現頁面展現和業務邏輯之間的分離。 在Flask中,模板被編寫為單獨的文件,存儲在應用程序包內的templates文件夾中。 在確定你在microblog目錄後,創建一個存儲模板的目錄:
在Flask中,模板是作為單獨的文件編寫的,存放在應用程序 包內的 templates文件夾(約定俗成命名為 templates)下。
即在app目錄下創建templates文件夾:
# mkdir templates在下面可以看到你的第一個模板,它的功能與上面的index()視圖函數返回的HTML頁面相似。 把這個文件寫在app/templates/index.html中:
<html>
<head>
<title>{{ title }} - Microblog</title>
</head>
<body>
<h1>Hello, {{ user.username }}!</h1>
</body>
</html>這是一個標準的、簡單的HTML頁面。但有一點跟我們寫HTML代碼不同是title標籤、h1標籤中的 {{ … }}兩對花括號,在此它是個 佔位符,作用是將表達式(如文字、數學式子、比較運算符等,其實在Python中是一個Python語句)打印到模板進行輸出。具體參考Jinja2官網文檔。
這些佔位符表示HTML頁面中可變的部分,並且只在運行時才知道。
這個HTML頁面看起來非常簡單,唯一值得關注的地方是{{ … }}。{{ … }}包含的內容是動態的,只有在運行時才知道具體表示成什麼樣子。
網頁渲染轉移到HTML模板之後,視圖函數就能被簡化:
現在HTML頁面的呈現已在HTML模板中了,這樣接着就可簡化一下視圖函數index()了。修改routes.py文件
app/routes.py:使用render_template()函數
from app import appfrom flask import render_template #從flask包中導入render_template函數
@app.route('/')
@app.route('/index')
def index():
user = {'username':'Miguel'}
return render_template('index.html', title='Home', user=user)將模板(index.html)轉換為完整HTML頁面的操作稱之為 呈現(render,譯作 遞交、表達、給予,在此譯作 “渲染”)。為了渲染模板,由從flask包中導入的render_template()完成,此函數“攜帶”模板文件名(index.html)、模板參數的變量列表,並返回相同的模板,不過其中所有佔位符都替換為實際值。
看起來好多了吧? 趕緊試試這個新版本的應用程序,看看模板是如何工作的。 在瀏覽器中加載頁面後,你需要從瀏覽器查看HTML源代碼並將其與原始模板進行比較。
render_template()函數調用Flask框架原生依賴的Jinja2模板引擎。 Jinja2用render_template()函數傳入的參數中的相應值替換{{…}}塊。
控制結構—-if條件、for循環、繼承
上述過程只是理解了:Jinja2模板引擎 如何在渲染過程中用實際值 替換 (模板中的)佔位符。
接下來將認識到更多 Jinja2在模板文件中支持的更多強大操作:if條件、for循環、繼承等。
源自官網的這句話:
There are a few kinds of delimiters. The default Jinja delimiters are configured as follows:
{% ... %} for Statements
{{ ... }} for Expressions to print to the template output
{# ... #} for Comments not included in the template output
# ... ## for Line Statementsif、for、繼承均在{% … %}塊中寫控制語句。
if條件
形如:
{% if title %}
...語句塊
{% else %}
...語句塊
{% endif %}app/templates/index.html:模板中添加條件語句
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog!</title>
{% endif %}
</head>
<body>
<h1>Hello,{{ user.username }}!</h1>
</body>
</html>上述代碼中if語句塊的功能是:若視圖函數index()沒有傳遞title佔位符變量的值,則index.html模板將會提供默認值(else語句塊中),而不是顯示空標題。
嘗試將routes.py中render_template()中的title=’Home’,刪除。效果:圖略
for循環
在模板中形如:
{% for post in posts %}
...語句塊
{% endfor %}需求:登錄用戶可在主頁中查看最新的帖子。
實現:
首先,用虛擬對象的方法來創建一些用戶、帖子,以供顯示。
app/routes.py:視圖函數中的假帖子
from app import appfrom flask import render_template#從flask包中導入render_template函數
@app.route('/')
@app.route('/index')
def index():
user = {'username':'Miguel'}#用戶
posts = [#創建一個列表:帖子。裡面元素是兩個字典,每個字典里元素還是字典,分別作者、帖子內容。
{
'author': {'username':'John'},
'body':'Beautiful day in Portland!'},
{
'author': {'username':'Susan'},
'body':'The Avengers movie was so cool!'
}
]
return render_template('index.html', title='Home', user=user, posts=posts)帖子列表 可包含任意數量的元素,由視圖函數index()決定將在頁面中顯示的帖子數量。而模板index.html不能假設這有多少個帖子,因此它需要準備好以通用方式呈現視圖發送來的儘可能多的帖子。在模板index.html中,用for循環遍歷所有的帖子並呈現。
app/templates/index.html:在模板中的for循環
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog!</title>
{% endif %}
</head>
<body>
<h1>Hello,{{ user.username }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}
</body>
</html>模板繼承
形如:
{% extends "base.html" %}
{% block content %}
...
{% endblock %}現在,大部分Web應用程序在頁面頂部有一個導航欄,它常包含一些常用鏈接:如登錄、退出、編輯個人資料等。可以很輕鬆地將導航欄添加到index.html模板,甚至更多的HTML頁面中。但隨着應用程序的增長(頁面數量的增多),這些頁面都將使用相同的導航欄,不可能每一個頁面都增加一份相同的導航欄代碼。
Jinja2具有模板繼承功能,完美解決上述問題。在實際操作中,將所有模板共有的頁面布局部分移至基礎模板中,其他模板則繼承自它。
實例:實現一個簡單的導航欄,其他模板繼承它。
在app/templates目錄下創建一個基礎模板文件 base.html。
app/templates/base.html:帶導航欄的基礎模板
<html>
<head>
{% if title %}
<title>{{ title }} - Microblog</title>
{% else %}
<title>Welcome to Microblog</title>
{% endif %}
</head>
<body>
<div>Microblog:<a href="/index">Home</a></div> <hr>
{% block content %}
{% endblock %}
</body>
</html>在上述基礎模板中,塊block 控制語句用於定義派生模板可自行插入的位置。塊block 被賦予唯一的名字 content,派生模板在提供其內容時可引用這個名稱。
修改index.html這個模板,讓其繼承base.html模板。
app/templates/index.html:從基礎模板繼承
{% extends "base.html" %}
{% block content %}
<h1>Hello,{{ user.username }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}
{% endblock %}base.html基礎模板 實現處理常規頁面的結構,則派生模板index.html簡化大部分內容。
extends語句 建立了兩個模板之間的繼承關係,因此,Jinja2就會知道:當它被要求渲染index.html時,需要將其嵌入base.html中。這倆模板具有匹配的block語句 名稱content,這就是Jinja2如何將兩個模板合併為一個模板的方法。

今後,當再需要為應用程序創建其他頁面時,就可省去編寫相同代碼的麻煩,並讓應用程序的所有頁面共享相同的外觀,而只需一個步驟:創建繼承自base.html模板的派生模板。
目前為止,項目結構:
microblog/
venv/
app/
templates/
base.html
index.html
__init__.py
routes.py
microblog.py原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/222376.html
微信掃一掃
支付寶掃一掃