Flask開發輕部落格(二):Flask-模板
作者:chen_h
微訊號 & QQ:862251340
微信公眾號:coderpai
目錄
上節回顧
如果你依照上一章的話,你應當有一個完全工作的簡單的 web 應用程式,我們專案的檔案結構如下(前提是使用sudo apt-get install tree
命令安裝 tree
,然後輸入 tree micblog
就可以得到類似於以下結構的檔案樹):
進入 micblog
資料夾,執行 python run.py
來執行應用程式,接著在你的網頁瀏覽器上開啟 http://127.0.0.1:9999
網址就可以看到效果(確保虛擬環境 flask 是啟用的)。
在 Python 中生成 HTML 並不好玩,實際上是相當繁瑣的,因為你必須自行做好 HTML 轉義以保持應用程式的安全。由於這個原因,Flask 自動為你配置好Jinja2 模版。我們將會在這一章中介紹一些模板基本概念以及基本用法。
我們接下來講述的正是我們上一章離開的地方,所以你可能要確保應用程式 micblog 正確地安裝和工作。
一、為什麼我們需要模板
讓我們來考慮下我們該如何擴充我們這個小的應用程式。
我們希望我們的微博應用程式的主頁上有一個歡迎登入使用者的標題,這是這種型別的應用程式的一個“標配”。忽略本應用程式暫未有使用者的事實,我會在後面的章節引入使用者的概念。
輸出一個漂亮的大標題的一個容易的選擇就是使得我們的檢視 micblog/app/views.py
檔案中的函式 index()
返回HTML,也許可以這樣編寫程式碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from app import app
@app.route('/')
@app.route('/index')
def index():
user = { 'nickname': 'Miguel' }
return '''
<html>
<head>
<title> Home Page </title>
</head>
<body>
<h1> Hello, ''' + user['nickname'] + ''' </h1>
</body>
</html>
'''
現在看看網頁瀏覽器上的顯示情況。
我們暫時還不支援使用者,所以暫時使用佔位符的使用者物件,有時也被稱為假冒或模仿的物件。這樣讓我們可以集中關注應用程式的某一方面,而不用花心思在暫未完成的部分上。
我希望你同意我的說法,上面的解決方案是非常難看!如果我們需要返回一個含有大量動態內容的大型以及複雜的 HTML 頁面的話,程式碼將會有多麼複雜啊!如果你需要改變你的網站佈局,在一個大的應用程式,該應用程式有幾十個檢視,每一個直接返回HTML?這顯然不是一個可擴充套件的選擇。
二、模板從天而降
如果你能夠保持你的應用程式與網頁的佈局或者介面邏輯上是分開的,這樣顯得更加容易組織!你甚至可以聘請一個網頁設計師來設計一個殺手級的網頁而你專注於 Python 編碼。模板可以幫助實現這種分離。
讓我們編寫第一個我們的模板(檔案 app/templates/index.html):
<html>
<head>
<title>{{ title }} - microblog</title>
</head>
<body>
<h1>Hello, {{ user.nickname }}!</h1>
</body>
</html>
正如你在上面看到,我們只是寫了一個部分標準的HTML頁面,唯一的區別是有一些動態內容的在 {{ … }} 中。
現在看看怎樣在我們的檢視函式(檔案 app/views.py)中使用這些模板:
#-*- coding:utf-8 -*-
from flask import render_template
from app import app
@app.route('/')
@app.route('/index')
def index():
user = { 'nickname': 'Miguel' } # 使用者名稱
return render_template("index.html",
title = 'Home',
user = user)
試著執行下應用程式看看模板是如何工作的。一旦在你的網頁瀏覽器上呈現該網頁,你可以瀏覽下 HTML 原始碼,與原始的模板內容對比下差別。
為了渲染模板,我們必須從 Flask 框架中匯入一個名為 render_template 的新函式。此函式需要傳入模板名以及一些模板變數列表,返回一個所有變數被替換的渲染的模板。
在內部,render_template 呼叫了 Jinja2 模板引擎,Jinja2 模板引擎是 Flask 框架的一部分。Jinja2 會把模板引數提供的相應的值替換了 {{…}} 塊。
1. 模板中控制語句
Jinja2 模板同樣支援控制語句,像在 {%…%} 塊中。讓我們在我們的模板中新增一個 if
宣告(檔案 app/templates/index.html):
<html>
<head>
{% if title %}
<title>{{ title }} - microblog</title>
{% else %}
<title>Welcome to microblog</title>
{% endif %}
</head>
<body>
<h1>Hello, {{ user.nickname }}!</h1>
</body>
</html>
現在我們的模板變得更加智慧了。如果檢視函式忘記輸入頁面標題的引數,不會觸發異常反而會出現我們自己提供的標題。放心地去掉檢視函式中 render_template 的呼叫中的 title 引數,看看 if 語句是如何工作的!
2. 模板中的迴圈語句
在我們 microblog 應用程式中,登入的使用者想要在首頁展示他的或者她的聯絡人列表中使用者最近的文章,因此讓我們看看如何才能做到。
首先我們先建立一些使用者以及他們的文章用來展示(檔案 app/views.py):
def index():
user = { 'nickname': 'Miguel' } # 使用者名稱
posts = [ # 提交內容
{
'author': { 'nickname': 'John' },
'body': 'Beautiful day in Portland!'
},
{
'author': { 'nickname': 'Susan' },
'body': 'The Avengers movie was so cool!'
}
]
return render_template("index.html",
title = 'Home',
user = user,
posts = posts)
為了表示使用者的文章,我們使用了列表,其中每一個元素包含 author 和 body 欄位。當我們使用真正的資料庫的時候,我們會保留這些欄位的名稱,因此我們在設計以及測試模板的時候儘管使用的是假冒的物件,但不必擔心遷移到資料庫上更新模板。
在模板這一方面,我們必須解決一個新問題。列表中可能有許多元素,多少篇文章被展示將取決於檢視函式。模板不會假設有多少文章,因此它必須準備渲染檢視傳送的文章數量。
因此讓我們來看看怎麼使用 for 來做到這一點(檔案 app/templates/index.html):
<html>
<head>
{% if title %}
<title>{{ title }} - microblog</title>
{% else %}
<title>microblog</title>
{% endif %}
</head>
<body>
<h1>Hi, {{ user.nickname }}!</h1>
{% for post in posts %}
<p>{{ post.author.nickname }} says: <b>{{ post.body }}</b></p>
{% endfor %}
</body>
</html>
簡單吧?試試吧,確保給予足夠的文章列表。關於前臺的一些html
學習,推薦線上學習網站W3School。
3. 模板繼承
在這一章結束前我們將討論最後一個話題。
在我們的 microblog 應用程式中,在頁面的頂部需要一個導航欄。在導航欄裡面有編輯賬號,登出等等的連結。
我們可以在 index.html 模板中新增一個導航欄,但是隨著應用的擴充套件,越來越多的模板需要這個導航欄,我們需要在每一個模板中複製這個導航欄。然而你必須要保證每一個導航欄都要同步,如果你有大量的模板,這需要花費很大的力氣。
相反,我們可以利用 Jinja2 的模板繼承的特點,這允許我們把所有模板公共的部分移除出頁面的佈局,接著把它們放在一個基礎模板中,所有使用它的模板可以匯入該基礎模板。
所以讓我們定義一個基礎模板,該模板包含導航欄以及上面談論的標題(檔案 app/templates/base.html):
<html>
<head>
{% if title %}
<title>{{ title }} - microblog</title>
{% else %}
<title>microblog</title>
{% endif %}
</head>
<body>
<div>Microblog: <a href="/index">Home</a></div>
<hr>
{% block content %}{% endblock %}
</body>
</html>
在這個模板中,我們使用 block 控制語句來定義派生模板可以插入的地方。塊被賦予唯一的名字。
接著現在剩下的就是修改我們的 index.html 模板繼承自 base.html (檔案 app/templates/index.html)::
{% extends "base.html" %}{% block content %}
<h1>Hi, {{ user.nickname }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.nickname }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}{% endblock %}
4. 最終效果圖
如果以上程式執行正確,那麼你講得到如下的實驗效果圖。