開始決定認真的在網上寫一些東西,主要原因還是在於希望能提升學習效果。雖說python寫了有幾年,但是web後端框架的確沒怎麼接觸過,買了本狗書寥寥草草的過了一遍,發現很多東西還是理解不深,真的是好記性不如爛筆頭,知識也要從基礎開始,退回來好好看看官方文件,再去看狗書吧。
網上有翻譯好的官方文件,基本是基於0.10.1版本翻譯的,和目前版本對比了一下,細節上還是有一些不同(狗書也存在這個問題),所以還是老老實實的看英文原版學習吧,目前的版本是0.12.2
“微型”的含義
眾所周知,flask是一個使用Python開發的“微型”Web框架,文件中特意強調了,所謂“微型”並不意味著Web應用的開發只能寫在一個Python檔案裡,也不意味著flask自身功能不夠豐富。“微型”的目的在於,保持一個“簡單”並且“可擴充套件”的框架核心,為開發者提供一個選擇自由的Web框架。基於此,開發者可以自由的選擇資料庫或模板引擎,為自己的Web應用做合適的選擇。
最小應用
通過flask實現一個Hello World只需要幾行程式碼
from flask import Flask
app = Flask(__name__)
@app.route(`/`)
def hello_world():
return `Hello, World!`
把一頭大象放進冰箱只需三步:
- 引用Flask類,並建立Flask類的例項。這個例項就是HelloWorld應用的WSGI介面。
- 編寫hello_world函式,返回`hello, World!`字串訊息。
- 使用route裝飾器,為hello_world建立路徑為`/`的路由。
這個示例程式碼與0.10版不同,在舊版文件中,通過在程式碼中新增app.run()方法來執行這個Web應用,而在0.12版文件中,應用的啟動工作使用了命令列的方式來處理:
$ export FLASK_APP=hello.py
$ flask run
* Running on http://127.0.0.1:5000/
或者:
$ export FLASK_APP=hello.py
$ python -m flask run
* Running on http://127.0.0.1:5000/
通過給FLASK_APP環境變數賦值,告訴flask它的web應用是哪個。我嘗試了在程式碼中使用app.run()方法啟動,也一樣可以執行。
from flask import Flask
app = Flask(__name__)
@app.route(`/`)
def hello_world():
return `Hello World!`
if __name__ == `__main__`:
app.run(`0.0.0.0`)
Debug模式
開啟debug模式可實現程式碼變更的熱載入,即:程式碼修改後,不需要重啟flask server,就可以自動觸發變更程式碼的載入。
另外,開啟debug模式可以實現在頁面檢視執行中的錯誤資訊,追蹤錯誤發生原因,適合開發過程中的錯誤除錯。
開啟Debug模式的方法包括:
$ export FLASK_DEBUG=1
$ flask run
以及在程式碼中,為Flask類例項的run方法中,指明debug引數為True:
app.run(debug=True)
路由
在Flask下,使用者可以使用@app.route()裝飾器為頁面設計具有可讀性的靜態路由,也可以在路由部分中加入變數,以使路由動態可變。
路由的形式類似於linux下的檔案路徑。
示例如下:
#靜態路由
@app.route(`/hello`)
def hello():
return `Hello, World`
#使用動態變數的路由(未指定變數型別)
@app.route(`/user/<username>`)
def show_user_profile(username):
# show the user profile for that user
return `User %s` % username
#使用動態變數的路由(指定變數型別)
@app.route(`/post/<int:post_id>`)
def show_post(post_id):
# show the post with the given id, the id is an integer
return `Post %d` % post_id
指定的路由變數,可以作為被裝飾的函式引數傳入進來。當路由尾部加入`/`時,無論在瀏覽器位址列中輸入的網址尾部是否加`/`,瀏覽器都會自動重定向到有/的路由上。
url_for()函式
flask模組提供了url_for()函式用於獲取函式的URL,因此在專案中所有引用到URL字串的地方,都可以使用url_for(func, **kwargs)來獲取函式的URL,這樣做的好處在於,可以使專案更容易維護。當某函式URL發生變更時,只需修改一處地方即可,而無須修改每一處URL引用。個人認為,在開發過程的任何時候,使用硬編碼都是極為不妥的。
HTTP方法
http作為客戶端與服務端的互動協議,包含了不同型別的請求方法(method),Flask中,最長使用的是GET、PUT和POST方法,一條路由適用哪種方法,可以在route裝飾器中定義。如不明確定義,route裝飾器中預設定義為GET方法。
官方例子:
from flask import request
@app.route(`/login`, methods=[`GET`, `POST`])
def login():
if request.method == `POST`:
do_the_login()
else:
show_the_login_form()
http還包括HEAD以及OPTIONS方法,在較新的Flask中,已經為使用者內部實現,因此一般開發過程中無需在意。
靜態檔案
Flask的靜態檔案目錄預設為專案目錄內的static目錄,一般所有靜態檔案都存放在這個目錄內。
模板渲染
Flask內建了Jinja2作為模板引擎,並提供render_template方法用於模板渲染,使用起來很方便,只需在方法內指定需要渲染的html檔名稱,並傳入模板變數值即可實現模板渲染。
官方示例:
from flask import render_template
@app.route(`/hello/`)
@app.route(`/hello/<name>`)
def hello(name=None):
return render_template(`hello.html`, name=name)
預設情況下,Flask會到當前專案目錄下的templates目錄內尋找模板檔案。
官方模板檔案示例:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
模板檔案之間可實現繼承,這一特性保證了不同頁面內的重複頁面元素(頁頭、頁尾、導航欄)等,可通過模板繼承的方式迅速實現。
訪問請求資料
來自客戶端的http請求,在server端以request物件存在。
Flask為每一個request建立一個處理執行緒,並線上程內部建立上下文實現執行緒安全。因此開發者在開發過程中不需要為執行緒安全費太多心思。
關於request物件,獲取客戶端傳輸來的資料方式非常簡單,類似於字典的形式,只需要在request.method中指明key的值即可實現:
#獲取登入表單資料
@app.route(`/login`, methods=[`POST`, `GET`])
def login():
error = None
if request.method == `POST`:
if valid_login(request.form[`username`],
request.form[`password`]):
return log_the_user_in(request.form[`username`])
else:
error = `Invalid username/password`
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template(`login.html`, error=error)
文件在此處還提及了檔案上傳場景以及cookie的簡單用法,後續文件應當有更加詳細的記錄,在此不詳述。
錯誤與重定向
Flask使用redirect()函式處理重定向邏輯。使用abort(error_code)處理錯誤返回。
若希望對錯誤頁面進行定製,可使用errorhandler(error_code)裝飾器修飾對應的檢視函式,定義本地錯誤頁面。
關於應答
flask有自己的應答處理邏輯,可大致總結為:
1.檢視函式返回字串時,flask會自動將返回字串封裝如標準response物件內
2.使用者也可以在檢視函式內使用make_response()函式建立response物件並返回,這樣做的意義是,在返回前使用者可以對response物件的部分內容進行設定,例如cookie。
3.如果返回物件是一個tuple,那麼內容順序格式應滿足(response, status, headers)這樣的格式
會話session
session記錄了客戶端與server之間的一些資訊,官方文件給出了使用者登陸狀態的例子。通過判斷session中是否存在username鍵值來判斷使用者是否已登入,以此為依據返回不同的展示內容。
示例如下:
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route(`/`)
def index():
if `username` in session:
return `Logged in as %s` % escape(session[`username`])
return `You are not logged in`
@app.route(`/login`, methods=[`GET`, `POST`])
def login():
if request.method == `POST`:
session[`username`] = request.form[`username`]
return redirect(url_for(`index`))
return ```
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
```
@app.route(`/logout`)
def logout():
# remove the username from the session if it`s there
session.pop(`username`, None)
return redirect(url_for(`index`))
# set the secret key. keep this really secret:
app.secret_key = `A0Zr98j/3yX R~XHH!jmN]LWX/,?RT`
使用session的前提是必須設定金鑰,金鑰的獲取可以使用python提供的隨機函式生成。
>>> import os
>>> os.urandom(24)
日誌模組
flask的日誌模組與python的logging模組使用方式類似,應該是做了內部整合,程式碼形式略微不同,在此做記錄:
app.logger.debug(`A value for debugging`)
app.logger.warning(`A warning occurred (%d apples)`, 42)
app.logger.error(`An error occurred`)