追溯到最初,Flask 誕生於 Armin Ronacher 在 2010 年愚人節開的一個玩笑。後來,它逐漸發展成為一個成熟的 Python Web 框架,越來越受到開發者的喜愛。目前它在 GitHub 上是 Star 數量最多的 Python Web 框架,沒有之一。
Flask 是典型的微框架,作為 Web 框架來說,它僅保留了核心功能:請求響應處理和模板渲染。這兩類功能分別由 Werkzeug(WSGI 工具庫)完成和 Jinja(模板渲染庫)完成,因為 Flask 包裝了這兩個依賴,我們暫時不用深入瞭解它們。
主頁
這一章的主要任務就是為我們的程式編寫一個簡單的主頁。主頁的 URL 一般就是根地址,即 /
。當使用者訪問根地址的時候,我們需要返回一行歡迎文字。這個任務只需要下面幾行程式碼就可以完成:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Welcome to My Watchlist!'複製程式碼
按照慣例,我們把程式儲存為 app.py,確保當前目錄是專案的根目錄,然後在命令列視窗執行 flask run
命令啟動程式(按下 Control + C 可以退出):
$ flask run
* Serving Flask app "app.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)複製程式碼
現在開啟瀏覽器,訪問 http://localhost:5000 即可訪問我們的程式主頁,並看到我們在程式裡返回的問候語,如下圖所示:
執行 flask run
命令時,Flask 會使用內建的開發伺服器來執行程式。這個伺服器預設監聽本地機的 5000 埠,也就是說,我們可以通過在位址列輸入 http://127.0.0.1:5000 或是 http://localhost:5000 訪問程式。
注意 內建的開發伺服器只能用於開發時使用,部署上線的時候要換用效能更好的伺服器,我們會在最後一章學習。
解剖時間
下面我們來分解這個 Flask 程式,瞭解它的基本構成。
首先我們從 flask
包匯入 Flask
類,通過例項化這個類,建立一個程式物件 app
:
from flask import Flask
app = Flask(__name__)複製程式碼
接下來,我們要註冊一個處理函式,這個函式是處理某個請求的處理函式,Flask 官方把它叫做檢視函式(view funciton),你可以理解為“請求處理函式”。
所謂的“註冊”,就是給這個函式戴上一個裝飾器帽子。我們使用 app.route()
裝飾器來為這個函式繫結對應的 URL,當使用者在瀏覽器訪問這個 URL 的時候,就會觸發這個函式,獲取返回值,並把返回值顯示到瀏覽器視窗:
@app.route('/')
def hello():
return 'Welcome to My Watchlist!'複製程式碼
填入 app.route()
裝飾器的第一個引數是 URL 規則字串,這裡的 /
指的是根地址。
我們只需要寫出相對地址,主機地址、埠號等都不需要寫出。所以說,這裡的 /
對應的是主機名後面的路徑部分,完整 URL 就是 http://localhost:5000/。如果我們這裡定義的 URL 規則是 /hello
,那麼完整 URL 就是 http://localhost:5000/hello 。
整個請求的處理過程如下所示:
- 當使用者在瀏覽器位址列訪問這個地址,在這裡即 http://localhost:5000/
- 伺服器解析請求,發現請求 URL 匹配的 URL 規則是
/
,因此呼叫對應的處理函式hello()
- 獲取
hello()
函式的返回值,處理後返回給客戶端(瀏覽器) - 瀏覽器接受響應,將其顯示在視窗上
提示 在 Web 程式的語境下,雖然客戶端可能有多種型別,但在本書裡通常是指瀏覽器。
程式發現機制
如果你把上面的程式儲存成其他的名字,比如 hello.py,接著執行 flask run
命令會返回一個錯誤提示。這是因為 Flask 預設會假設你把程式儲存在名為 app.py 或 wsgi.py 的檔案中。如果你使用了其他名稱,就要設定系統環境變數 FLASK_APP
來告訴 Flask 你要啟動哪個程式。
Flask 通過讀取這個檔案對應的模組尋找要執行的程式例項,你可以把它設定成下面這些值:
- 模組名
- Python 匯入路徑
- 檔案目錄路徑
管理環境變數
現在在啟動 Flask 程式的時候,我們通常要和兩個環境變數打交道:FLASK_APP
和 FLASK_ENV
。因為我們的程式現在的名字是 app.py,暫時不需要設定 FLASK_APP
;FLASK_ENV
用來設定程式執行的環境,預設為 production
。在開發時,我們需要開啟除錯模式(debug mode)。除錯模式可以通過將系統環境變數 FLASK_ENV
設為 development
來開啟。除錯模式開啟後,當程式出錯,瀏覽器頁面上會顯示錯誤資訊;程式碼出現變動後,程式會自動過載。
為了不用每次開啟新的終端會話都要設定環境變數,我們安裝用來管理系統環境變數的 python-dotenv:
$ pipenv install python-dotenv複製程式碼
當 python-dotenv 安裝後,Flask 會從專案根目錄的 .flaskenv 和 .env 檔案讀取環境變數並設定。我們分別使用文字編輯器建立這兩個檔案,或是使用更方便的 touch
命令建立:
$ touch .env .flaskenv複製程式碼
.flaskenv 用來儲存 Flask 命令列系統相關的公開環境變數;而 .env 則用來儲存敏感資料,不應該提交進Git倉庫,我們把 .env 新增到 .gitignore 檔案的結尾(新建一行)來讓 Git 忽略它。你可以使用編輯器執行這個操作:
.env複製程式碼
在新建立的 .flaskenv 檔案裡,我們寫入一行 FLASK_ENV=development
,將環境變數 FLASK_ENV
的值設為 development
,以便開啟除錯模式:
FLASK_ENV=development複製程式碼
實驗時間
在這個小節,我們可以通過做一些實驗,來擴充套件和加深對本節內容的理解。
修改檢視函式返回值
首先,你可以自由修改檢視函式的返回值,比如:
@app.route('/')
def hello():
return u'歡迎來到我的 Watchlist!'複製程式碼
返回值作為響應的主體,預設會被瀏覽器作為 HTML 格式解析,所以我們可以新增一個 HTML 元素標記:
@app.route('/')
def hello():
return '<h1>Hello Totoro!</h1><img src="http://helloflask.com/totoro.gif">'複製程式碼
儲存修改後,只需要在瀏覽器裡重新整理頁面,你就會看到頁面上的內容也會隨之變化。
修改 URL 規則
另外,你也可以自由修改傳入 app.route
裝飾器裡的 URL 規則字串,但要注意以斜線 /
作為開頭。比如:
@app.route('/home')
def hello():
return 'Welcome to My Watchlist!'複製程式碼
儲存修改,這時重新整理瀏覽器,則會看到一個 404 錯誤提示,提示頁面未找到(Page Not Found)。這是因為檢視函式的 URL 改成了 /home
,而我們重新整理後訪問的地址仍然是舊的 /
。如果我們把訪問地址改成 http://localhost:5000/home,就會正確看到返回值。
一個檢視函式也可以繫結多個 URL,這通過附加多個裝飾器實現,比如:
@app.route('/')
@app.route('/index')
@app.route('/home')
def hello():
return 'Welcome to My Watchlist!'複製程式碼
現在無論是訪問 http://localhost:5000/、http://localhost:5000/home 還是 http://localhost:5000/index 都可以看到返回值。
在前面,我們之所以把傳入 app.route
裝飾器的引數稱為 URL 規則,是因為我們也可以在 URL 裡定義變數部分。比如下面這個檢視函式會處理所有類似 /user/<name>
的請求:
@app.route('/user/<name>')
def user_page():
return 'User page'複製程式碼
不論你訪問 http://localhost:5000/user/greyli,還是 http://localhost:5000/user/peter,抑或是 http://localhost:5000/user/甲,都會觸發這個函式。通過下面的方式,我們也可以在檢視函式裡獲取到這個變數值:
@app.route('/user/<name>')
def user_page(name):
return 'User: %s' % name複製程式碼
修改檢視函式名?
最後一個可以修改的部分就是檢視函式的名稱了。首先,檢視函式的名字是自由定義的,和 URL 規則無關。和定義其他函式或變數一樣,只需要讓它表達出所要處理頁面的含義即可。
除此之外,它還有一個重要的作用:作為代表某個路由的端點(endpoint),同時用來生成 URL。對於程式內的 URL,為了避免手寫,Flask 提供了一個 url_for
函式來生成 URL,它接受的第一個引數就是端點值,預設為檢視函式的名稱:
from flask import url_for
...
@app.route('/')
def hello():
return 'Hello'
@app.route('/user/<name>')
def user_page(name):
return 'User: %s' % name
@app.route('/test')
def test_url_for():
# 下面是一些呼叫示例:
print(url_for('hello')) # 輸出:/
# 注意下面兩個呼叫是如何生成包含 URL 變數的 URL 的
print(url_for('user_page', name='greyli')) # 輸出:/user/greyli
print(url_for('user_page', name='peter')) # 輸出:/user/peter
print(url_for('test_url_for')) # 輸出:/test
# 下面這個呼叫傳入了多餘的關鍵字引數,它們會被作為查詢字串附加到 URL 後面。
print(url_for('test_url_for', num=2)) # 輸出:/test?num=2
return 'Test page'複製程式碼
實驗過程中編寫的程式碼可以刪掉,也可以保留,但記得為根地址返回一行問候,這可是我們這一章的任務。
本章小結
這一章我們為程式編寫了主頁,同時學習了 Flask 檢視函式的基本編寫方式。結束前,讓我們提交程式碼:
$ git add .
$ git commit -m "Add minimal home page"
$ git push複製程式碼
為了保持簡單,我們統一在章節最後一次提交所有改動。在現實世界裡,通常會根據需要分為多個 commit;同樣的,這裡使用 -m
引數給出簡單的提交資訊。在現實世界裡,你可能需要撰寫更完整的提交資訊。
提示 你可以在 GitHub 上檢視本書示例程式的對應 commit:016302a
進階提示
- 如果你使用 Python 2.7,為了使程式正常工作,需要在指令碼首行新增編碼宣告
# -*- coding: utf-8-*-
,並在包含中文的字串前面新增u
字首。本書中對於包含中文的字串均新增了u
字首,這在 Python 3 中並不需要。 - 對於 URL 變數,Flask 還支援在 URL 規則字串裡對變數設定處理器,對變數進行預處理。比如
/user/<int:number>
會將 URL 中的 number 部分處理成整型,同時這個變數值接收傳入數字。 - 因為 Flask 的上下文機制,有一些變數和函式(比如
url_for
函式)只能在特定的情況下才能正確執行,比如檢視函式內。我們先暫時不用糾結,後面再慢慢了解。 - 名字以
.
開頭的檔案預設會被隱藏,執行ls
命令時會看不到它們,這時你可以使用ls -f
命令來列出所有檔案。 - 瞭解 HTTP 基本知識將會有助於你瞭解 Flask 的工作原理。
- 閱讀文章《網際網路是如何工作的》。
- 閱讀文章《從HTTP請求 - 響應迴圈探索Flask的基本工作方式》。
- 如果你是《Flask Web 開發實戰》的讀者,這部分的進階內容可以在第 1 章《初識 Flask》和第 2 章《HTTP 和 Flask》找到。