《Flask 入門教程》 第 2 章:Hello, Flask!

追溯到最初,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 就是 localhost:5000/hello

整個請求的處理過程如下所示:

  1. 當用戶在瀏覽器地址欄訪問這個地址,在這裡即 localhost:5000/
  2. 伺服器解析請求,發現請求 URL 匹配的 URL 規則是 /,因此調用對應的處理函數 hello()
  3. 獲取 hello() 函數的返回值,處理後返回給客戶端(瀏覽器)
  4. 瀏覽器接受響應,將其顯示在窗口上

提示 在 Web 程序的語境下,雖然客戶端可能有多種類型,但在本書里通常是指瀏覽器。

程序發現機制

如果你把上面的程序保存成其他的名字,比如 hello.py,接著執行 flask run 命令會返回一個錯誤提示。這是因為 Flask 默認會假設你把程序存儲在名為 app.py 或 wsgi.py 的文件中。如果你使用了其他名稱,就要設置系統環境變數 FLASK_APP 來告訴 Flask 你要啟動哪個程序。

Flask 通過讀取這個文件對應的模塊尋找要運行的程序實例,你可以把它設置成下面這些值:

  • 模塊名
  • Python 導入路徑
  • 文件目錄路徑

管理環境變數

現在在啟動 Flask 程序的時候,我們通常要和兩個環境變數打交道:FLASK_APPFLASK_ENV。因為我們的程序現在的名字是 app.py,暫時不需要設置 FLASK_APPFLASK_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 還是 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:eca06dc。

進階提示

  • 如果你使用 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》找到。
  • 本書主頁 & 相關資源索引:helloflask.com/tutorial

推薦閱讀:

TAG:Flask | Python | Web開發 |