一篇文章入門Flask
Flask本身相當於一個核心,其他幾乎所有的功能都要用到擴充套件(郵件擴充套件Flask-Mail,使用者認證Flask-Login),都需要用第三方的擴充套件來實現。
Flask對WSGI (路由)的實現,是採用 Werkzeug,而模板引擎(業務檢視) 則使用 Jinja2。這兩個是Flask框架的核心。
Flask核心就是作為一個Webapp框架的兩個基礎部分:
- CGI (WSGI) 路由:是由Werkzeug實現,即將網路請求翻譯成Python語言
- Template 業務模版:是由Python流行的Jinja2實現
除此之外,Flask其它一切的都是由第三方外掛實現:
包括:
Flask-SQLalchemy:操作資料庫; Flask-migrate:管理遷移資料庫; Flask-Mail:郵件; Flask-WTF:表單; Flask-Bable:提供國際化和本地化支援,翻譯; Flask-script:插入指令碼; Flask-Login:認證使用者狀態; Flask-OpenID:認證; Flask-RESTful:開發REST API的工具; Flask-Bootstrap:整合前端Twitter Bootstrap框架; Flask-Moment:本地化日期和時間; Flask-Admin:簡單而可擴充套件的管理介面的框架
參考Flask擴充套件列表: http://flask.pocoo.org/extens...
Flask安裝
安裝很簡單:
$ pip install flask
建議在Virtualenv虛擬環境下安裝,因為Flask需要一系列的依賴,最好給Flask生成一個專用的生產環境,並生產 requirement.txt
依賴列表:
# 生產虛擬環境 $ virtualenv ./flask_env # 啟動虛擬環境 $ ./flask_env/bin/active # 生產依賴列表 $ pip freeze > requirements.txt
Hello World
一個最簡單的Flask程式,只需要三步:
路由規則 app.run()
, hello-world.py
:
from flask import Flask # 生產一個Flask APP 例項,並指向當前檔案(模組) app = Flask(__name__) # 指定"/"根目錄的路由規則 @app.route('/') def index(): return 'Hello World' # 開始執行app app.run()
Flask中,路由的實現是用裝飾器: @app.route("/")
這種方式來做到的。
Flask路由規則
頁面顯示指定內容:
@app.route('/') def index(): return '<h1> 歡迎訪問此頁面 </h1>'
頁面返回指定內容、狀態碼、headers等:
@app.route('/') def index(): body = '<h1> 歡迎訪問此頁面 </h1>' status_code = 200 headers = {'Cache-Control':'no-cache', 'Connection':'keep-alive'} return body, status_code, headers
指定接收的 請求方式
:
@app.route('/', method='GET') # ...
給路由傳引數:
@app.route('/orders/<order_id>') def hello_itheima(order_id): return 'The ID of order is: %d' % order_id
限制路由引數的資料型別:
@app.route('/orders/<int:order_id>') # ...
其中 int:order_id
是指定引數 order_id
必須能轉換成int整數,否則這個請求就會自動被拒絕。
比如使用者請求 http://xyz.com/orders/HAHAHAH
,這就不成功。而 http://xyz.com/orders/210
這樣的就成功。
中斷請求:
# ... from flask import abort @app.route('/wrong-page') def hello(): # 中斷請求,並返回404狀態碼 abort(404)
處理錯誤請求:
# ... @app.errorhandler(404) def handel_404_error(): return '<h1> The page does not exist </h1>'
其中,如果路由中呼叫了 abort(404)
中斷函式,或是其它產生404錯誤的方法,這個 app.errorhandler
都會被自動呼叫。
返回“渲染”過的模版(即把動態的模版渲染成靜態的HTML):
#... from flask import render_template @app.route('/') def index(): return render_template('index.html')
Flask 路由分割
如果所有路徑的路由都定義在一個檔案裡,會很難維護,所以必定要把各個路徑的路由分拆到不同的檔案裡。
這種分拆很簡單:
正常情況下,在 index.py
主模組中,我們還是一樣正常的定義路由:
#... @app.route('/') def index(): pass
然後我們可以把其它路由的處理函式分別放在別的檔案裡,比如:
register.py
中定義“普通函式”:
def reg(): pass
以及 login.py
中定義一個普通函式:
def signin(): pass
然後回到主模組 index.py
中,我們可以匯入這些函式,並顯式的將這些函式註冊到路由上:
from flask import Flask from register import reg from login import signin app = Flask(__name__) app.route('/register')(reg) app.route('/login')(signin) @app.route('/') def index(): pass
然後我們可以用 app.url_map
獲得當前定義過的所有路由:
print( app.url_map )
Flask Blueprint 藍圖
我們手動分割路由處理函式,然後分別匯入,這樣雖然也簡單,但是不不好的地方是,主模組外定義的各個處理函式,本身很難看出來處理的是什麼路由邏輯。
為此,Flask提供了另一種 路由分割
的方法:即 Blueprint
類。
而這個Blueprint類生成的物件,是在子模組中代替了之前我們所使用的 Flask
類生成的app物件。
也就是說:主模組還是用app,但是子模組中用藍圖blueprint。
假設我們現在有一個子模組 order.py
定義 "/order"
路徑的路由,那麼檔案中定義如下:
from flask import Blueprint # 生成藍圖例項:引數中一個是藍圖名稱,一個是主模組名稱 app_orders = Bluepint('blueprint_orders', __name__) # 將路由新增到藍圖裡 @app_orders.route('/orders') def get_orders(): pass
然後回到主模組 index.py
中,把藍圖註冊到主路由上:
#... from orders import app_orders app = Flask(__name__) app.register_blueprint( app_orders ) #...
Hooks 鉤子事件
Flask提供一個完整請求至迴應的事件流,其中包括:
@app.before_first_request @app.before_request @app.route() @app.after_request
- @app.teardown_request
: 關閉請求時,即每次請求是否異常都會被執行
以下是鉤子的用法:
#... @app.before_first_request def handle_before_first_request(): pass @app.route('/') def index(): pass @app.... def ...
Flask上下文請求物件 flask.current_app
request.current_app
是Flask特有的一種 request請求處理
方式,不同於 flask.request
物件的處理方式,它是能區分多個請求的。
在我們常用的 flask.request
物件中,會有一個很嚴重的問題:即它是一個 全域性變數
。也就是說,如果伺服器在處理併發請求時使用的是在同一個程序裡的多執行緒,那麼不同使用者的請求也許會使用同一個 flask.request
物件!這時候request中的請求資訊就會出現混淆!
所以Flask引入了 request.current_app
這個物件,即它能夠根據上下文來區分不同人的請求。
這是怎麼做到的呢?其實很簡單,它只是把request變為一個 區域性變數
而已。這樣一來,每次的request請求,都是各自獨立的區域性物件。
返回響應資訊 flask.make_response
除了我們自己定義返回的資訊外,Flask提供了一個內建的 make_response
物件,便於處理返回資訊。
返回全文資訊:
from flask import make_response @app.route('/') def index(): resp = make_response('<h1> 歡迎訪問此頁面 </h1>') resp.status = 200 resp.headers['Cache-Control'] = 'no-cache' return resp
設定cookies:
from flask import make_response @app.route('/') def index(): resp = make_response('<h1> 此頁面會設定你的cookies :) </h1>') resp.set_cookie('uuid', '1230sfjdlsj3uu') resp.set_cookie('name', 'Jason', max_age=360) return resp
其中, max_age
是cookie的存活時間,以s秒為單位。不設定的話,預設是 臨時cookies
,即瀏覽器關閉後立馬失效。
刪除cookie: resp.delete_cookie('uuid')
。注意,這裡的刪除並不是立馬刪除瀏覽器中使用者的cookie,而只是把 max_age
設定為0,即瀏覽器關閉後立馬失效。
獲取請求資訊 flask.request
Flask中有一個request物件,接收了一切對當前模組的請求資料。
使用的話,直接在 @app.route
後面的函式中用 def index(request)
接收來自裝飾器的請求物件即可使用。
request引數型別:
常用的各種型別操作如下:
from flask import request app = Flask(__name__) @app.route('/', method='POST') def index(request): # Get uploaded file afile = request.files.get('pic') with open('./pic.jpg', 'w') as f: f.write( afile.read() ) # Get a form form = request.from# Dict型別 name = form.get('name') age = form.get('age') # Get cookies uuid = request.cookies.get('uuid') name = request.cookies.get('name')
會話處理 flask.session
在登入頁設定session,並在index頁根據session判斷是否登入:
from flask import session #... app.config['SECRET_KEY'] = 'asdlkjflaj23jrsdjf任意字串作為金鑰kaljdsl;fkja;j' @app.route('/login') def login(): # 設定sessions session['uuid'] = '123abadsf' return '<p> 登入成功 </p>' @app.route('/') def index(): # 獲取sessions uuid = session['uuid'] # 判別session是否存在 if uuid: return '<p> 之前已登入過 </p>' else: return '<p> 未登入,請重新登入 </p>'
其中,Flask預設情況下,會利用 app.config['SECRET_KEY']
的值作為一個金鑰,來加密你手動設定的session,然後把這個資訊轉換為名叫 session
的cookie存在瀏覽器中。
這個是Flask特別的一點。
但是把敏感的session資料儲存到誰都能訪問的cookie中,即使加密了也不是很安全。
所以一般我們還是會手動把session資料存到伺服器後臺的資料庫中,而不是存到cookie中。
每次驗證再與資料庫進行對比。
表單處理 request.form
動態網頁必須要的就是Form表單。Flask中有自帶的form表單處理方法。不過我們也可以用第三方外掛 Flask-WTF
實現。
這裡我們先只講自帶的處理方式。
Flask自帶表單處理
假設我們有一個表單模版 form.html
:
<form method="post"> 使用者名稱:<input type="text" name="username"> 密碼: <input type="password" name="password"> 確認密碼: <input type="password" name="password2"> <input type="submit" value="提交"><br> {% for message in get_flashed_messages() %} {{ message }} {% endfor %} </form>
當用戶點選submit提交時,
整個form資訊就會用POST方式提交到Flask的路由檔案 abc.py
中。
我們進行處理如下:
from flask import Flask from flask import render_template from flask import request app.secret_key = 'abc123' @app.route('/', methods=['GET', 'POST']) def hello(): if request.method == 'POST': # 獲取引數, 並效驗引數完整性, 如果有問題就進行flash username = request.form.get('username') password = request.form.get('password') password2 = request.form.get('password2') if not all([username, password, password2]): flash('params error') elif password != password2: flash('password error') else: print username return 'success' return render_template('Congratulations.html')
Flask的HTTP Server
一般我們在開發除錯過程中,可以用Flask自帶的WSGI和一個小HTTP Server來實現整個App正常運轉。
但是生產環境中,這兩個自帶的元件就效率很低了。所以我們需要用效率更高的獨立的CGI和獨立的HTTP Server伺服器來部署真正的生產環境
一般常見的選項有:
- HTTP Server -> 首推Nginx
- CGI翻譯器 -> Gunicorn (Python開發,實現了WSGI翻譯)
所以,我們一般採用 Nginx + Gunicorn + Flask
來部署網路應用。
Gunicorn的使用:
# 安裝 $ pip install gunicorn # 進入Flask app的主目錄 cd ./myFlask # 用gunicorn伺服器啟動Flask app $ gunicorn -w 4 -b 127.0.0.1:8080 main:app
這個時候,flask就在gunicorn的HTTP伺服器上運行了,可以通過127.0.0.1:8080訪問到app。