07.flask部落格專案實戰二之模板使用
配套視訊教程
模板定義
如有一個預期:html主頁有一個 歡迎使用者的標題。目前這個應用程式還沒使用者的概念,也沒使用者系統。但可用一個模擬使用者,用Python字典實現:
user = {'username':'Miguel'}
建立模擬物件是一種有用的技術,使我們可專注於應用程式已有的部分,而不必擔心尚不存在的部分。
app/routes.py:從檢視函式中返回完整的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> '''
在cmd中執行程式。更新檢視功能,並檢視應用程式在瀏覽器中的顯示效果。
C:\Users\Administrator>d: D:\microblog>cd D:\microblog\venv\Scripts D:\microblog\venv\Scripts>activate (venv) D:\microblog\venv\Scripts>cd D:\microblog (venv) D:\microblog>set FLASK_APP=microblog.py (venv) D:\microblog>flask run * Serving Flask app "microblog.py" * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 127.0.0.1 - - [02/Aug/2018 18:11:46] "GET / HTTP/1.1" 200 -
很明顯,HTML程式碼 和.py程式碼混在一起了,很不利於今後的維護、理解。就得將網路佈局(呈現)與應用程式的邏輯處理程式碼分開/分離。模板在此閃亮登場!將助實現表示、業務邏輯的分離。
在Flask中,模板是作為單獨的檔案編寫的,存放在應用程式包內的templates資料夾(約定俗成命名為 templates)下。
即在app目錄下建立templates資料夾:
(venv) D:\microblog>cd D:\microblog\app
(venv) D:\microblog\app>mkdir templates
在此目錄下建立一個HTML檔案index.html
,它將和上方index()
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模板中了,這樣接著就可簡化一下檢視函式index()
了。修改routes.py檔案
app/routes.py:使用render_template()
函式
from app import app
from 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)、模板引數的變數列表,並返回相同的模板,不過其中所有佔位符都替換為實際值。
render_template()函式
呼叫與Flask框架捆綁在一起的Jinja2模板引擎。Jinja2會用相應的值替換{{ ... }}塊
,這個相應的值由render_template()
呼叫時提供的引數給出。
參考:flask.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 Statements
if、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 app
from 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://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-ii-templates