作者選擇了自由開源基金作為Write for DOnations計劃的一部分進行捐贈。
簡介
Flask是一個輕量級的Python網絡框架,為使用Python語言創建網絡應用程序提供了有用的工具和功能。
在開發網絡應用程序時,將業務邏輯與展示邏輯分開是很重要的。業務邏輯是處理用戶請求並與數據庫通信以構建適當響應的部分。展示邏輯是如何將數據呈現給用戶,通常使用HTML文件構建響應網頁的基本結構,並使用CSS樣式來設計HTML組件。例如,在社交媒體應用程序中,您可能有一個用戶名欄位和一個密碼欄位,只有在用戶未登錄時才會顯示。如果用戶已登錄,則顯示登出按鈕。這是展示邏輯。如果用戶輸入了他們的用戶名和密碼,您可以使用Flask執行業務邏輯:從請求中提取數據(用戶名和密碼),如果憑證正確則登錄用戶或回應錯誤信息。錯誤信息的顯示將由展示邏輯處理。
在 Flask 中,您可以使用 Jinja 模板語言來渲染 HTML 模板。模板是一個文件,可以包含固定和動態內容。當用戶從您的應用程序請求某些內容(例如索引頁或登錄頁)時,Jinja 允許您以一個 HTML 模板進行響應,在這個模板中,您可以使用標準 HTML 不具備的許多功能,例如變量、if
語句、for
循環、過濾器和模板繼承。這些功能使您能夠高效地編寫易於維護的 HTML 頁面。Jinja 還會自動進行 HTML 轉義,以防止 跨站腳本攻擊 (XSS)。
在本教程中,您將構建一個小型 Web 應用程序,渲染多個 HTML 文件。您將使用變量將數據從服務器傳遞到模板。模板繼承將幫助您避免重複。您將在模板中使用條件和循環等邏輯,使用過濾器修改文本,並使用 Bootstrap 工具包 來設計您的應用程序。
先決條件
-
一個本地的 Python 3 編程環境。請按照 如何安裝和配置 Python 3 的本地編程環境系列中適用於您發行版的教程進行操作。在本教程中,我們將我們的項目目錄稱為
flask_app
。 -
在您的編程環境中安裝 Flask,具體步驟參見 如何使用 Flask 和 Python 創建您的首個 Web 應用程序中的第 1 步。
-
對基本 Flask 概念的理解,例如路由和視圖函數。如果您不熟悉 Flask,請查看如何使用 Flask 和 Python 創建您的首個 Web 應用程序。
-
對基本 HTML 概念的理解。您可以回顧我們的如何使用 HTML 構建網站教學系列以獲取背景知識。
步驟 1 — 渲染模板並使用變量
確保你已經激活了你的環境並且已經安裝了 Flask,然後你就可以開始構建你的應用程序了。第一步是在首頁上顯示一條歡迎訪客的消息。你將使用 Flask 的 render_template()
輔助函數來提供一個 HTML 模板作為響應。你也將看到如何從你的應用程序端向模板傳遞變量。
首先,在你的 flask_app
目錄中,打開一個名為 app.py
的文件進行編輯。使用 nano
或你喜歡的文本編輯器:
在 app.py
文件中添加以下代碼:
保存並關閉文件。
在這段代碼中,你從 flask
包中導入了 Flask
類和 render_template()
函數。你使用 Flask
類來創建你的 Flask 應用程序實例,名為 app
。然後你定義了一個 視圖函數(這是一個返回 HTTP 響應的 Python 函數),稱為 hello()
,使用 app.route()
裝飾器將一個普通函數轉換為視圖函數。這個視圖函數使用 render_template()
函數來渲染一個名為 index.html
的模板文件。
接下來,你需要在flask_app
目錄內的templates
目錄中創建一個名為index.html
的模板文件。Flask會在名為templates
的模板目錄中尋找模板,因此這個名稱很重要。確保你位於flask_app
目錄中,並運行以下命令來創建templates
目錄:
接著,打開templates
目錄中的index.html
文件進行編輯。這裡的index.html
名稱並非標準要求的名稱;你可以將其命名為home.html
、homepage.html
或其他任何名稱:
在index.html
文件中添加以下HTML代碼:
在這裡,你設置了一個標題,添加了一條Hello World!
消息作為H1
標題,並創建了一條Welcome to FlaskApp!
消息作為H2
標題。
保存並關閉文件。
在激活虛擬環境的情況下,進入你的flask_app
目錄,使用FLASK_APP
環境變量告訴Flask應用程序的位置(在你的情況下是app.py
),並將FLASK_ENV
環境變量設置為development
,以開發模式運行應用程序並獲得調試器的訪問權限。使用以下命令來完成這些操作(在Windows上,使用set
而不是export
):
然後,使用flask run
命令運行應用程序:
在開發服務器運行的情況下,使用你的瀏覽器訪問以下URL:
http://127.0.0.1:5000/
您會看到頁面的標題設置為 `FlaskApp
`,兩個標題是渲染後的 HTML。
在網絡應用程序中,您經常需要將數據從應用程序的 Python 文件傳遞到 HTML 模板。為了演示如何在這個應用程序中實現這一點,您將傳遞一個包含當前 UTC 日期和時間的變量到索引模板,並在模板中顯示該變量的值。
保持服務器運行,並在新終端中打開您的 `app.py
` 文件進行編輯:
從 Python 標準庫中導入 `datetime
` 模塊,並編輯 `index()
` 函數,使文件如下所示:
import datetime
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello():
return render_template('index.html', utc_dt=datetime.datetime.utcnow())
保存並關閉文件。
在這裡,您導入了 `datetime
` 模塊,並將一個名為 `utc_dt
` 的變量傳遞給 `index.html
` 模板,其值為 `datetime.datetime.utcnow()
`,即當前 UTC 日期和時間。
接下來,要在索引頁面上顯示變量的值,打開 `index.html
` 文件進行編輯:
編輯文件如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FlaskApp</title>
</head>
<body>
<h1>Hello World!</h1>
<h2>Welcome to FlaskApp!</h2>
<h3>{{ utc_dt }}</h3>
</body>
</html>
保存並關閉文件。
您添加了一個 H3 標題,使用特殊的 `{{ ... }}
` 分隔符來打印 `utc_dt
` 變量的值。
打開您的瀏覽器並訪問索引頁面:
http://127.0.0.1:5000/
您將看到類似下圖的頁面:
你已經在 Flask 應用程式中創建了一個包含 HTML 模板的索引頁面,渲染了一個模板,並傳遞和顯示了一個變數值。接下來,你將通過使用模板繼承來避免程式碼重複。
步驟 2 — 使用模板繼承
在這一步中,你將製作一個基礎模板,其中包含可以與其他模板共享的內容。你將編輯你的索引模板以繼承自基礎模板。然後,你將創建一個新頁面,作為你的應用程式的關於頁面,用戶可以在這裡找到更多關於你的應用程式的資訊。
一個 基礎模板 包含通常在所有其他模板之間共享的 HTML 組件,例如應用程式的標題、導航欄和頁尾。
首先,在你的模板目錄中打開一個名為 base.html
的新文件進行編輯:
在你的 base.html
文件中寫入以下代碼:
保存並關閉文件。
這個文件中的大部分代碼是標準的 HTML,一個標題,一些導航連結的樣式,一個包含兩個連結的導航欄,一個用於索引頁面,另一個用於尚未創建的關於頁面,以及一個用於頁面內容的 <div>
。(這些連結還未生效;下一個步驟將展示如何連接不同頁面)。
然而,以下高亮部分是專門針對 Jinja 模板引擎的:
-
{% block title %} {% endblock %}
: 一個區塊,作為標題的佔位符。稍後您將在其他模板中使用它,為應用程式中的每個頁面提供自訂標題,而無需每次都重寫整個<head>
部分。 -
{% block content %} {% endblock %}
: 另一個區塊,將根據子模板(繼承自base.html
的模板)來替換內容,該子模板將覆蓋它。
現在您已經有了基礎模板,可以利用繼承來使用它。打開index.html
文件:
然後將其內容替換為以下內容:
在這裡,您使用 `{% extends %}
` 標籤來繼承自 `base.html
` 模板。然後通過替換基礎模板中的 `content
` 區塊,擴展它為前面代碼區塊中的 `content
` 區塊內容。
這個內容區塊包含一個 `<h1>
` 標籤,內含文字 `Index
`,位於標題區塊內,這樣就替換了 `base.html
` 模板中原有的 `title
` 區塊,使其文字變為 `Index
`,從而完整的標題變成 `Index - FlaskApp
`。這樣一來,您可以避免重複相同的文字兩次,因為它既作為頁面的標題,又作為繼承自基礎模板的導航欄下方顯示的標題。
接著還有幾個標題:一個帶有文字 `Hello World!
` 的 `<h1>
` 標題,一個 `<h2>
` 標題,以及一個包含 `utc_dt
` 變數值的 `<h3>
` 標題。
模板繼承讓您能夠重複使用其他模板(此例中為 `base.html
`)中的 HTML 代碼,而無需每次需要時都重複編寫。
保存並關閉文件,然後刷新瀏覽器中的索引頁面。頁面將顯示如下:
接下來,您將創建關於頁面。打開 `app.py
` 文件以添加一個新路由:
在文件末尾添加以下路由:
在這裡,您使用 app.route()
裝飾器來創建一個名為 about()
的視圖函數。在其中,您返回調用 render_template()
函數的結果,並將 about.html
模板文件名作為參數傳遞。
保存並關閉文件。
打開一個名為 about.html
的模板文件進行編輯:
在文件中添加以下代碼:
在此,您使用 extends
標籤繼承自基礎模板,將基礎模板的 content
區塊替換為一個 <h1>
標籤,該標籤同時作為頁面的標題,並添加一個包含應用程式相關信息的 <h3>
標籤。
保存並關閉文件。
在開發服務器運行時,使用瀏覽器訪問以下 URL:
http://127.0.0.1:5000/about
您將看到類似以下的頁面:
注意導航欄和部分標題是如何從基礎模板繼承的。
您現在已經創建了一個基礎模板,並在您的索引頁和關於頁中使用它以避免代碼重複。此時,導航欄中的鏈接不起作用。在下一步中,您將學習如何通過修復導航欄鏈接在模板中路由之間進行鏈接。
步驟 3 — 鏈接頁面之間
在這一步中,你將學習如何使用url_for()
輔助函數在你的模板中鏈接不同頁面。你將在基礎模板中的導航欄添加兩個鏈接,一個指向索引頁,另一個指向關於頁。
首先打開你的基礎模板進行編輯:
編輯文件使其看起來如下:
在這裡,你使用了特殊的url_for()
函數,它將返回你給定的視圖函數的URL。第一個鏈接指向hello()
視圖函數的路由(即索引頁)。第二個鏈接指向about()
視圖函數的路由。注意,你傳遞的是視圖函數的名稱,而不是路由(/
或 /about
)。
使用url_for()
函數來構建URL有助於更好地管理URL。如果你硬編碼URL,當你修改路由時,你的鏈接將會中斷。使用url_for()
,你可以編輯路由並保證鏈接仍能正常工作。url_for()
函數還會處理其他事情,比如轉義特殊字符。
保存並關閉文件。
現在前往索引頁並嘗試導航欄中的鏈接。你會看到它們如預期般工作。
你學會了如何使用 `url_for()
` 函數在模板中連結到其他路由。接下來,你將添加一些條件語句來根據你設定的條件控制模板中顯示的內容,並在模板中使用 `for
` 循環來顯示列表項目。
步驟 4 — 使用條件和循環
在這一步中,你將在模板中使用 `if
` 語句來根據特定條件控制顯示內容。你還將使用 `for
` 循環遍歷 Python 列表並顯示列表中的每個項目。你將添加一個新頁面來顯示評論列表。索引號為偶數的評論將有藍色背景,而索引號為奇數的評論將顯示為灰色背景。
首先,你將為評論頁面創建一個路由。打開你的 `app.py
` 文件進行編輯:
在文件末尾添加以下路由:
在上面的路由中,你有一個名為 `comments
` 的 Python 列表,其中包含四個項目。(在現實場景中,這些評論通常來自數據庫,而不是像你在這裡硬編碼的那樣。)在最後一行,你返回一個名為 `comments.html
` 的模板文件,並將包含列表的變量 `comments
` 傳遞給模板文件。
保存並關閉文件。
接下來,在 templates
目錄中打開一個新的 comments.html
文件進行編輯:
將以下代碼添加到文件中:
在這裡,您擴展了 base.html
模板並替換了 content
塊的內容。首先,您使用一個 <h1>
標題,同時也作為頁面的標題。
您在 {% for comment in comments %}
這一行中使用 Jinja 的 for
循環來遍歷 comments
列表中的每個評論(這些評論會存儲在 comment
變量中)。您在 <p style="font-size: 24px">{{ comment }}</p>
標籤中顯示評論,就像在 Jinja 中通常顯示變量一樣。您使用 {% endfor %}
關鍵字標記 for
循環的結束。這與 Python 的 for
循環構造不同,因為在 Jinja 模板中沒有特殊的縮進。
保存並關閉文件。
在開發服務器運行時,打開瀏覽器並訪問評論頁面:
http://127.0.0.1:5000/comments
您將看到類似以下的頁面:
現在,您將在模板中使用 if
條件語句,通過顯示奇數索引號的評論帶有灰色背景,偶數索引號的評論帶有藍色背景。
打開您的 comments.html
模板文件進行編輯:
編輯它使其看起來如下:
在這次編輯中,你在行{% if loop.index % 2 == 0 %}
中添加了一個if
語句。loop
變數是一個特殊的Jinja變數,它讓你能夠訪問當前循環的資訊。在這裡,你使用loop.index
來獲取當前項目的索引,該索引從1
開始,而不是像Python列表那樣從0
開始。
這裡的if
語句通過使用%
操作符來檢查索引是否為偶數。它檢查索引數除以2
的餘數;如果餘數是0
,則意味著索引數是偶數,否則索引數是奇數。你使用{% set %}
標籤來聲明一個名為bg_color
的變數。如果索引數是偶數,你將其設置為藍色調的顏色,否則如果索引數是奇數,你將bg_color
變數設置為灰色。然後你使用bg_color
變數來設置包含評論的<div>
標籤的背景顏色。在評論文本上方,你使用loop.index
來在一個<p>
標籤中顯示當前的索引數。
保存並關閉文件。
打開你的瀏覽器並訪問評論頁面:
http://127.0.0.1:5000/comments
你將看到你的新評論頁面:
這是一個展示如何使用if
語句的示範。但你也可以通過使用特殊的loop.cycle()
Jinja助手來達到同樣的效果。為了演示這一點,打開comments.html
文件:
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %} Comments {% endblock %}</h1>
<div style="width: 50%; margin: auto">
{% for comment in comments %}
<div style="padding: 10px;
background-color: {{ loop.cycle('#EEE', '#e6f9ff') }};
margin: 20px">
<p>#{{ loop.index }}</p>
<p style="font-size: 24px">{{ comment }}</p>
</div>
{% endfor %}
</div>
{% endblock %}
在此,您移除了if/else
語句,並使用loop.cycle('#EEE', '#e6f9ff')
輔助工具在兩種顏色間循環。background-color
的值將一次為#EEE
,另一次為#e6f9ff
。
保存並關閉檔案。
在瀏覽器中打開評論頁面,刷新它,您會看到這與if
語句的效果相同。
您可以使用if
語句進行多種用途,包括控制頁面上顯示的內容。例如,為了顯示除第二條評論外的所有評論,您可以使用條件為loop.index != 2
的if
語句來過濾掉第二條評論。
打開評論模板:
並編輯成如下所示:
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %} Comments {% endblock %}</h1>
<div style="width: 50%; margin: auto">
{% for comment in comments %}
{% if loop.index != 2 %}
<div style="padding: 10px;
background-color: #EEE;
margin: 20px">
<p>#{{ loop.index }}</p>
<p style="font-size: 24px">{{ comment }}</p>
</div>
{% endif %}
{% endfor %}
</div>
{% endblock %}
在此,您使用{% if loop.index != 2 %}
來顯示索引不是2
的評論,這意味著除第二條評論外的所有評論。您還使用硬編碼的背景顏色值而非loop.cycle()
輔助工具以簡化操作,其餘部分未變。您使用{% endif %}
結束if
語句。
保存並關閉檔案。
刷新評論頁面,您會看到第二條評論未被顯示。
現在,您需要在導航欄中添加一個帶用戶到評論頁面的鏈接。打開基礎模板進行編輯:
編輯<nav>
標籤的內容,新增一個<a>
連結:
在這裡,你使用url_for()
輔助函數來連結到comments()
視圖函數。
保存並關閉文件。
導覽列現在會有一個新的連結,連結到評論頁面。
你在模板中使用了if
語句來根據特定條件控制顯示內容。你使用了for
循環來遍歷Python列表並顯示每個項目,並學習了Jinja中的特殊loop
變數。接下來,你將使用Jinja過濾器來控制變數數據的顯示方式。
步驟5 — 使用過濾器
在這一步中,你將學習如何在模板中使用Jinja過濾器。你將使用upper
過濾器將前一步中添加的評論轉換為大寫,使用join
過濾器將一系列字符串合併為一個字符串,並學習如何使用safe
過濾器在不轉義的情況下渲染受信任的HTML代碼。
首先,你將把評論頁面中的評論轉換為大寫。打開comments.html
模板進行編輯:
編輯使其看起來如下:
在這裡,您使用管道符號(|
)添加了 upper
過濾器。這將修改 comment
變數的值,使其變為大寫。
保存並關閉文件。
在開發伺服器運行時,使用瀏覽器打開評論頁面:
http://127.0.0.1:5000/comments
您可以看到,應用過濾器後,所有評論都變成了大寫。
過濾器也可以接受括號中的參數。為了演示這一點,您將使用 join
過濾器將 comments
列表中的所有評論連接起來。
打開評論模板:
編輯它使其如下所示:
在這裡,您添加了一個 <hr>
標籤和一個 <div>
標籤,在其中使用 join()
過濾器將 comments
列表中的所有評論連接起來。
保存並關閉文件。
刷新評論頁面,您將看到類似以下的頁面:
如您所見,comments
列表顯示時,評論之間用管道符號分隔,這正是您傳遞給 join()
過濾器的內容。
另一個重要的過濾器是 safe
過濾器,它允許你在瀏覽器上渲染受信任的 HTML。為了說明這一點,你將在評論模板中使用 {{ }}
Jinja 分隔符添加一些包含 HTML 標籤的文本。在實際場景中,這將作為變量從服務器傳遞過來。然後你將編輯 join()
參數,使其變為 <hr>
標籤而不是管道符號。
打開評論模板:
將其編輯如下:
在這裡,你添加了值 "<h1>COMMENTS</h1>"
並將 join 參數更改為 <hr>
標籤。
保存並關閉文件。
刷新評論頁面,你會看到類似以下的頁面:
如你所見,HTML 標籤未被渲染。這是 Jinja 的一項安全功能,因為某些 HTML 標籤可能有害,可能導致 跨站腳本 (XSS) 攻擊。你應該只允許受信任的 HTML 在瀏覽器中渲染。
要渲染上述 HTML 標籤,打開評論模板文件:
通過添加 safe
過濾器進行編輯:
你可以看到,你也可以像在這行 <p>{{ comments | join(" <hr> ") | safe }}</p>
中那樣鏈接過濾器。每個過濾器都應用於前一個過濾的結果。
保存並關閉文件。
刷新評論頁面,你會看到 HTML 標籤現在按預期渲染:
注意:在處理來自未知數據源的HTML時使用safe
過濾器可能會使您的應用程序面臨XSS攻擊的風險。除非您渲染的HTML來自可信來源,否則不要使用它。
更多信息,請查看內置Jinja過濾器列表。
您現在已經學會了如何在Jinja模板中使用過濾器來修改變量值。接下來,您將整合Bootstrap工具包來美化您的應用程序。
步驟6 — 整合Bootstrap
在這一步中,您將學習如何使用Bootstrap工具包來美化您的應用程序。您將在基礎模板中添加一個Bootstrap導航欄,該導航欄將出現在所有繼承自基礎模板的頁面中。
Bootstrap工具包幫助您美化應用程序,使其更具視覺吸引力。它還將幫助您在網絡應用程序中加入響應式網頁,以便它在移動瀏覽器上運行良好,而無需編寫自己的HTML、CSS和JavaScript代碼來實現這些目標。
要使用Bootstrap,您需要將其添加到基礎模板中,以便在所有其他模板中使用。
打開您的 base.html
模板進行編輯:
將其編輯成如下所示:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<title>{% block title %} {% endblock %} - FlaskApp</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('hello') }}">FlaskApp</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('comments') }}">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">About</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
{% block content %} {% endblock %}
</div>
<!-- Optional JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
</body>
</html>
上述代碼大部分是使用 Bootstrap 所需的樣板代碼。您有一些 meta 標籤,在 <head>
部分有一個指向 Bootstrap CSS 文件的連結,並在底部有一個指向可選 JavaScript 的連結。代碼中高亮的部分包含在前幾步中解釋的 Jinja 代碼。注意您如何使用特定的標籤和 CSS 類來告訴 Bootstrap 如何顯示每個元素。
在上述的 <nav>
標籤中,您有一個帶有 navbar-brand
類的 <a>
標籤,這決定了導航欄中的品牌連結。在 <ul class="navbar-nav">
標籤內,您在 <li>
標籤中的 <a>
標籤內有常規的導航欄項目。
要了解更多關於這些標籤和 CSS 類的信息,請參閱 Bootstrap 組件。
保存並關閉文件。
在開發服務器運行時,使用瀏覽器打開索引頁面:
http://127.0.0.1:5000/
您將看到類似以下的頁面:
現在您可以使用 Bootstrap 組件來在所有模板中為您的 Flask 應用程序中的項目進行樣式設置。
結論
你現在已經知道如何在 Flask 網頁應用程式中使用 HTML 模板。你利用變數將資料從伺服器傳遞到模板,採用模板繼承來避免重複,並整合了如 if
條件式和 for
迴圈等元素,以及在不同頁面間進行連結。你學習了如何使用過濾器來修改文字和顯示受信任的 HTML,並將 Bootstrap 整合進你的應用程式中。
如果你想進一步了解 Flask,可以查看Flask 主題頁面。
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-templates-in-a-flask-application