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

greylihui發表於2018-12-11

追溯到最初,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 入門教程》 第 2 章:Hello, Flask!


執行 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

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

  1. 當使用者在瀏覽器位址列訪問這個地址,在這裡即 http://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">'複製程式碼

儲存修改後,只需要在瀏覽器裡重新整理頁面,你就會看到頁面上的內容也會隨之變化。


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


修改 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》找到。

相關文章