使用 Flask 製作網站
關於 Flask
Flask 一個是使用 Python 所撰寫的網站開發框架,跟其他的 Python 網站開發框架相比算是個輕量級的框架,沒有強迫開發者只能用某種資料庫或樣板引擎,有經驗的開發者可依個人能力及喜好選擇合適的套件來組裝、使用。Flask 本體雖然功能不算多,但週邊的擴充套件不少,透過這些週邊套件開發者們可以更快的建立功能完整的網站。
要用 Flask 做網站,第一件事就是要先把 Flask 給安裝好。雖然 Flask 是一個網站框架,但它其實也是個套件,所以跟其他套件一樣只要一行 pip install flask
就能安裝完 成。
不過為了不把環境搞得一團亂,在這個章節我會使用在「環境安裝」章節介紹過的 Poetry 來建立虛擬環境,如果忘了怎麼使用的可再去復習一下。覺得 Poetry 太複雜的話,venv 也是個不錯的選擇。是說如果不想這麼麻煩,只是想試一下 Flask 手感的話,直接用 pip install flask
也沒什麼問題。
建立虛擬環境
首先,我先建立一個全新的目錄 flask-demo
,待會虛擬環境以及專案的程式碼都會放在這裡:
$ mkdir flask-demo
$ cd flask-demo
$ poetry init -n
這裡我請 Poetry 幫我建立一個空的 pyproject.toml
檔案,接著我們就可以用 Poetry 啟動虛擬環境:
$ poetry shell
Creating virtualenv flask-demo in /tmp/flask-demo/.venv
Spawning shell within /tmp/flask-demo/.venv
$ (flask-demo-py3.12) %
因為使用了虛擬環境,所以就算之前曾經在電腦上安裝過其他版本的 Flask 也都沒關係,反正在這個全新的虛擬環境裡都一切重新來過。因為接下來都是在虛擬環境裡作業,所以 (flask-demo-py3.12)
的提示字元我就會適時的省略。搞定虛擬環境之後,接著就是要請 Poetry 來安裝 Flask:
$ poetry add flask
Using version ^3.0.3 for flask
Updating dependencies
Resolving dependencies... (1.7s)
Package operations: 7 installs, 0 updates, 0 removals
- Installing markupsafe (2.1.5)
- Installing blinker (1.8.2)
- Installing click (8.1.7)
- Installing itsdangerous (2.2.0)
- Installing jinja2 (3.1.4)
- Installing werkzeug (3.0.3)
- Installing flask (3.0.3)
Writing lock file
可以看的出來除了 Flask 之外,還多裝了好幾個相依套件。安裝完了 Flask 之後,然後 Flask 專案會長什麼樣子? 其他的網站開發框架可能有些指令可以建立全新的專案,在 Flask 有指令可以做這件事嗎?沒有,但其實也不太需要,因為隨便寫一個 .py
檔案可以開始用 Flask 了。
Hello, Flask!
首先,我先建立一個名為 hellokitty.py
檔案,然後在裡面寫入以下程式碼:
from flask import Flask
cat = Flask(__name__)
@cat.route("/")
def home():
return "<h1>Hello Flask!</h1>"
說明:
- 從
flask
套件中匯入Flask
類別,然後使用它建立一個物件叫做cat
。 - 在
home()
函數前面掛上一個函數的裝飾器,.route("/")
方法裡的"/"
代表根目錄,也就是網站的首頁。當有人訪問網站的根目錄時,就會執行這個函數,而執行這個函數會回傳一串文字。
這個函數裝飾器我們在前面的函數章節有介紹過,這個可以額外接參數的裝飾器的細節其實滿複雜的,但大概就是會幫我們加上路徑比對的功能,我們就不用像前一章在手刻 WSGI 一樣從環境變數判斷路徑。如果有需要,.route()
方法還可以掛好幾次,例如:
@cat.route("/")
@cat.route("/hi")
def home():
return "<h1>Hello Flask!</h1>"
這樣表示 /
跟 /hi
這兩個網址都會執行 home()
函數。
還記得在上一章講到 WSGI 的時候,最後要回傳一個可迭代物件,裡面的元素還必須是位元組,過程中還要設定 HTTP 狀態碼以及標頭,實在有點麻煩。Flask 幫我們搞定了這些雜事,像上面這樣簡單幾行就能搞定,我們只要把心力放在想要給使用者看到什麼內容就好。
另外,這裡大家會看到我檔名用 hellokitty.py
,然後變數用 cat
,好像都跟官網文件不太一樣,看起來好像在亂寫?的確是有點亂寫沒錯,這除了好玩之外,也是為了讓大家知道這些 檔名以及變數名稱都是可以自己決定的,並沒有規定一定要叫什麼名字。
就這樣短短幾行,我們就完成了一個超陽春的 Flask 程式。接著我們可以用 Flask 提供的指令來啟動網站:
$ flask --app hellokitty run
* Serving Flask app 'hellokitty'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
因為一開始我就手賤故意把檔案取名為 hellokitty.py
,所以這裡我需要額外加上 --app
選項來指定要執行的檔案。如果你的檔案跟官網文件一樣叫做 app.py
的話,那就不用這麼麻煩,直接執行 flask run
就行了。從執行之後的訊息可以看的出來 Flask 在本機的 Port 5000
上運行,所以只要打開瀏覽器連上 http://127.0.0.1:5000
就可以看到我們的網站了。
如果你仔細看啟動時候的訊息,還會發現有個貼心小提醒(其實是警告),說我們伺服器這個是開發用的,不適合用在正式環境,請改用比較厲害一點的 WSGI 伺服器來運行。別擔心,在最後我們也會介紹使用厲害一點的伺服器來執行我們的網站。
頁面不會更新?
如果你在啟動伺服器之後再去修改程式碼的話,你會發現頁面的內容再怎麼重新整理都不會跟著更新,你得先按 Ctrl + C
把原本的伺服器停掉,重新啟動,才會看 到更新之後的效果,這感覺有點沒效率。這是因為 Flask 預設不會監聽檔案的內容異動,我們可以在啟動的時候額外加上 --reload
選項,就可以有這個效果了:
$ flask --app hellokitty run --reload
* Serving Flask app 'hellokitty'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
你可以試著修改一下程式碼,然後再看看終端機的反應:
* Detected change in '/tmp/flask-demo/hellokitty.py', reloading
* Restarting with stat
一改完就馬上被 Flask 發現檔案有變動,然後就自動幫我們重新啟動,這樣就不用每次修改完都要重新啟動伺服器了。
偵錯模式
Flask 有提供一個偵錯小工具,可以讓我們在開發的過程中就能透過網頁介面來抓問題。要開始偵錯工具的話,在啟動的時候加上 --debug
選項:
$ flask --app hellokitty run --debug
* Serving Flask app 'hellokitty'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 279-046-439
可以在上面的啟動訊息裡看到 Debug mode: on
,這表示已經開啟偵錯模式,同時在最後一行還有提供 PIN 碼 279-046-439
,這組 PIN 碼在不同的專案或不同的電腦上的數字應該不會一樣,所以如果各位跟著我做到這裡,號碼應該不會跟我的一樣。如果想試玩看看,你可以故意寫錯一行程式碼,在瀏覽器上就會看到錯誤訊息的畫面,在錯誤訊息的右手邊有個小小的終端機按鈕,點下去就會跳出這個提示視窗:
貼上剛才產生的 PIN 碼,就能直接在網頁上抓問題。或是如果只是想看看偵錯模式長什麼樣子,也直接可以連上 127.0.0.1:5000/console
,貼上 PIN 碼,就可以進入偵錯模式了:
同時,如果開啟偵錯模式,就會自動帶有頁面重新載入的效果,所以可以不用另外加 --reload
選項。
然後,不要在正式環境使開啟偵錯模式!不要在正式環境使開啟偵錯模式!不要在正式環境使開啟偵錯模式!重要的事情要講三次。在自己本機或開發環境上測試還行,但不要在正式環境使用,不要以為有設定 PIN 碼鎖起來大家進不來,事實上就這幾個號碼的排列組合並不是多難試出來的。
指定 IP 位址和 Port 號
剛才介紹的是使用 Flask 提供的指令配合一些選項來啟動伺服器,如果想要改變伺服器啟動時候的 IP 位置或是改變 Port 號,只要在後面接上 --host
及 --port
選項就可以了,但我個人更偏好把這些選項直接寫 在程式碼裡,這樣就不用每次在輸入指令的時候都得加上好幾個選項。我把原來的程式碼改成這樣:
from flask import Flask
cat = Flask(__name__)
@cat.route("/")
def home():
return "<h1>Hello Flask!!</h1>"
if __name__ == "__main__":
cat.run(port=9527, debug=True)
我把啟動的程式碼寫在 __name__ == "__main__"
裡面,所以只有在直接執行這個檔案的時候才會啟動,也就是說,要啟動的時候就不是透過 flask run
.而是跟一般的執行 Python 程式一樣:
$ python hellokitty.py
* Serving Flask app 'hellokitty'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9527
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 279-046-439
可以看到 Port 號變了,而且偵錯模式也打開了。
新增頁面:關於我們
是說,做網站總不能只有一個首頁吧,好歹也要再來個關於我們之類的頁面。在上一章講到 WSGI 的時候有提到,如果要加入其他頁面可以透過判斷環境變數的路徑來顯示不同的內容,但在 Flask 中,我們可以透過函數裝飾器來做到這件事。假設我想新增一個路徑是 /about
的頁面,並且希望在畫面上顯示「關於我們」字樣:
from flask import Flask
cat = Flask(__name__)
@cat.route("/")
def home():
return "<h1>Hello Flask!</h1>"
@cat.route("/about")
def about_page():
return "<h1>關於我們</h1>"
if __name__ == "__main__":
cat.run(port=9527, debug=True)
這寫起來應該明顯比用傳統的 WSGI 要簡單很多(應該有吧?),不用在函數裡面判斷路徑,不用設定狀態跟表頭,也不用回傳什麼位元組串列,好寫多了。
但通常我們的頁面也不會只有一個簡單的 <h1>
標籤,如果說能把這些 HTML 寫在另外的檔案裡,應該會更方便好用...