NO IMAGE

Python超級明星WEB框架Flask

2015-11-04 14:36:22
Flask簡介Flask是一個相對於Django而言輕量級的Web框架。和Django大包大攬不同,Flask建立於一系列的開源軟體包之上,這其中 最主要的是WS

Flask簡介

Flask是一個相對於Django而言輕量級的Web框架。

和Django大包大攬不同,Flask建立於一系列的開源軟體包之上,這其中 最主要的是WSGI應用開發庫Werkzeug和模板引擎Jinja:

策略 :werkzeug和Jinja這兩個庫和Flask一樣,都是pocoo團隊開發的。這 或許體現了pocoo與Django競爭時關於生態的一種策略,這種策略的自然 延伸是Flask框架中沒有包含資料庫方面的構件,無論ORM還是其他。

關注點 :Flask是一個WSGI應用框架,這意味著我們進行Flask開發時,不需要 關注網路方面的操作,Flask應用的入口是封裝過的網路請求包,出口是 網路響應,我們僅需要關注這個階段內的處理邏輯。

WSGI伺服器 :Flask雖然內建了簡單的WSGI伺服器,但其效能僅僅適用於開發期的除錯。 Flask官網推薦了多種WSGI伺服器,實現方式從多程序到多執行緒到協程, 這方面的選擇我們在本課程中將不涉及。

REST適應性 :雖然Flask和Django一樣,最初的出發點都是服務端的動態網頁應用。但 Flask的設計使之也相當適用於面向資源的REST架構,在越來越移動化 並且單頁應用越來越重要的WEB開發領域,這是Flask相對於Django相當 大的優勢。

Hello Flask

編寫一個基於Flask的hello world相當容易:

1、匯入Flask類

1
from flask import Flask

Flask類是Flask框架的核心類,它實現了WSGI應用規範。

2、建立Flask例項

1
app = Flask(__name__)

Flask建構函式的第一個引數指定一個引入名/importname。Flask框架 使用這個名字進行靜態資源、模板、錯誤資訊的定位。除非你清楚的理解它的 作用,通常情況下,我們總應該使用特殊變數_name。

Flask例項是可呼叫的(具有call方法),這個例項可以直接對接 WSGI伺服器。

3、註冊路由

1
2
3
@route('/')
def index():
    return 'Hello,Flask!'

註冊路由就是建立URL規則和處理函式之間的關聯。Flask框架依賴於路由 完成HTTP請求的分發。

路由中的函式被稱為檢視函式,其返回值將作為HTTP響應的正文內容。

4、對接並啟動WSGI伺服器

Flask封裝了一個簡單的開發用WSGI伺服器,我們可以通過呼叫run() 啟動伺服器執行:

1
app.run(host='0.0.0.0',port=80)

概述

路由是MVC架構的Web框架中相當重要的一個概念,也是本節課程的重點。

顧名思意,路由就是在迷茫中找出一條路的意思。在Flask框架中,路由就表示為使用者請求的URL找出其對應的處理函式之意。

在本節課程,我們將主要從以下幾個方面講解Flask框架中的路由:

如何為應用註冊路由? 如何為路由指定其支援的HTTP方法? 如何匹配動態URL? 如何對URL中的變數型別進行過濾? 如何理解訪問點/endpoint? 如何為應用設定靜態路由? 如何避免硬編碼指向其他檢視的URL?

註冊路由

在Flask應用中,路由是指使用者請求的URL與檢視函式之間的對映。Flask框架 根據HTTP請求的URL在路由表中匹配預定義的URL規則,找到對應的檢視函式, 並將檢視函式的執行結果返回WSGI伺服器:

匯智網 flask route

可見路由表在Flask應用中處於相當核心的位置。路由表的內容是由應用開發者填充。

route裝飾器 :可以使用Flask應用例項的route裝飾器將一個URL規則繫結到 一個檢視函式上。

例如,下面的示例將URL規則/test繫結到檢視函式test()上:

1
2
3
@app.route('/test')
def test():
    return 'this is response'

如果這個應用部署在主機ezhost.com的根目錄下,那麼當使用者訪問:

http://pythontab.com/teset

Flask框架就會呼叫我們的test()函式,其返回結果就傳遞給WSGI伺服器傳送給訪問者。

add_url_rule() :另一種等價的寫法是使用Flask應用例項的add_url_route()方法。 下面的示例註冊了一個與前例相同的路由:

1
2
3
def test():
    return 'this is response'
app.add_url_route('/test',view_func=test)

其實,route裝飾器內部也是通過呼叫add_url_route()方法實現的路由註冊。 但是顯然,使用裝飾器使程式碼看起來更優雅一些。

為路由指定HTTP方法

預設情況下,Flask路由僅支援HTTP的GET請求。可以使用methods關鍵字引數,在註冊 路由時顯式地宣告檢視方法支援的HTTP方法。

例如,下面的示例將URL規則/auth繫結到檢視函式v_auth(),這個路由僅支援POST方法:

1
2
@app.route('/auth',methods=['POST'])
def v_auth():pass

指定多種HTTP方法支援

關鍵字引數methods的型別為list,因此可以同時指定多種HTTP方法。

下面的示例中,使URL規則/user同時支援POST方法和GET方法:

1
2
3
4
5
6
@app.route('/user',methods=['POST','GET'])
def v_users():
    if request.method == 'GET':
        return ... # 返回使用者列表
    if request.method == 'POST'
        return ... #建立新使用者

這個特性使Flask非常易於開發REST架構的後臺服務,而不僅僅侷限於傳統的動態網頁。

匹配動態URL

有時我們需要將同一類URL對映到同一個檢視函式處理,比如,使用同一個檢視函式 來顯示不同使用者的個人檔案。我們希望以下的URL都可以分發到同一個檢視函式:

匯智網 flask route-param

在Flask中,可以將URL中的可變部分使用一對小括號<>宣告為變數, 併為檢視函式宣告同名的引數:

1
2
3
@app.route('/user/')
def v_user(uname):
    return '%s\'s Profile' % uname

在上面的示例中,URL規則中的表示這部分是可變的,Flask將提取使用者請求的 URL中這部分的內容,並作為檢視函式v_user()的uname引數進行呼叫。

URL變數型別過濾

考慮下面的示例,我們希望通過HTTP共享資料夾/var/readonly中的檔案:

/var

    /readonly

        /a.txt

        /b.txt

        /repo

           /c.txt

           /d.txt

簡單思考一下就有答案了。我們可以構造URL規則/file/,然後直接 讀取檔案內容返回給使用者。註冊如下的路由:

1
2
3
4
5
6
7
@app.route('/file/')
def v_file(fname):
    fullname = os.path.join('/var/readonly',fname)
    = open(fullname)
    cnt =  f.read()
    f.close()
    return cnt

測試結果表明,/file/a.txt和/file/b.txt都沒有問題,但是/file/repo/c.txt和 /file/repo/d.txt卻會失敗。

這是因為,預設情況下,在URL規則中的變數被視為不包含/的字串。/file/repo/c.txt 是沒有辦法匹配URL規則/file/的。

可以使用內建的path轉換器告訴Flask框架改變這一預設行為。path轉換器允許 規則匹配包含/的字串:

1
@app.route('/file/')

在Flask中,轉換器/converter用來對從URL中提取的變數進行預處理,這個過程 發生在呼叫檢視函式之前。Flask預置了四種轉換器:

string – 匹配不包含/的字串,這是預設的轉換器

path – 匹配包含/的字串

int – 只有當URL中的變數是整型值時才匹配,並將變數轉換為整型

float – 只有當URL中的變數是浮點值時才匹配,並將變數轉換為浮點型

訪問點/endpoint

我們一直強調,路由的作用是根據請求的URL,找到對應的檢視函式。這沒錯,但是在 Flask框架中,請求任務的分發並不是直接從使用者請求的URL一步定位到檢視函式, 兩者之間隔著一個訪問點/endpoint。

以下面的程式碼為例,我們看Flask怎樣實現請求的分發:

1
2
@app.route('/home')
def home():pass

在Flask內部使用兩張表維護路由:

url_map :維護URL規則和endpoint的對映

view_functions :維護endpoint和檢視函式的對映。

以使用者訪問URL/home為例,Flask將首先利用url_map找到所請求URL對應的 endpoint,即訪問點home,然後再利用view_functions表查詢home這個訪問點 對應的檢視函式,最終匹配到函式home():

匯智網 flask endpoint.jpg

預設訪問點 :當我們使用route裝飾器註冊路由時,預設使用被裝飾函式的 函式名(name)作為訪問點,因此,你看到上面的表中,路由中的訪問點為home。

自定義訪問點 :可以在使用route裝飾器或呼叫add_url_rule()方法註冊路由時,使用 endpoint關鍵字引數改變這一預設行為:

1
2
@app.route('/home',endpoint='whocare')
def home():pass

此時的兩張路由表將變成這樣:

匯智網 flask endpoint2

靜態目錄路由

當建立應用例項時,Flask將自動新增一條靜態目錄路由,其訪問點 始終被設定為static,URL規則預設被設定為/static,本地路徑預設被 設定為應用資料夾下的static子資料夾:

+————————————————————+ | url rule | endpoint | view_function | | /static | static | Flask.send_static_file | +————————————————————+ 如果你的應用目錄如下:

/app

    /web.py

    /static

        /main.css

        /jquery.min.js   

那麼啟動應用後就可以通過URL/static/main.css訪問static資料夾下的main.css了。

除了訪問點被固定為static,靜態目錄的URL規則和本地目錄都是可以根據應用情況進行調整。

改變預設的本地路徑 :可以在建立應用物件時使用關鍵字引數static_folder改變 預設的靜態資料夾。例如,你的靜態檔案都存放在應用下的assets目錄下, 那麼可以按如下的方式建立應用物件:

app = Flask(name,static_folder=’assets’) 也可以使用一個絕對路徑:

app = Flask(name,static_folder=’/var/www/static’) 改變預設的本地路徑並不會對路由表產生影響。

改變預設的URL規則 : 如果不喜歡靜態目錄URL/static,也可以在建立應用 物件時使用關鍵字引數static_url_path換一個別的名字。

下面的示例中,將應用下的assets資料夾註冊為靜態目錄/assets:

app = Flask(name,static_folder=’assets’,static_url_path=’/assets’) 當應用執行後,通過URL/assets/main.css就可以訪問assets資料夾下的 main.css檔案了。

這時的路由表變化為:

+————————————————————+ | url | endpoint | view_function | | /assets | static | Flask.send_static_file | +————————————————————+

構造URL

在一個實用的檢視中,不可避免地存在指向其他檢視的連結。在之前的課程示例中,我們 都是在檢視函式中這樣硬編碼這些連結URL的:

1
2
3
4
5
@app.route('/')
def v_index():
    return 'tech'
@app.route('/tech'
def v_tech():pass

大部分情況下這種硬編碼URL是可以工作的。但如果這個應用被掛在WSGI伺服器的一個 子路徑下,比如:/app1,那麼使用者訪問URL/tech是不會成功的,這時應當訪問/app1/tech 才可以正確地路由到檢視函式v_tech()。

我們應當使用訪問點讓Flask框架幫我們計算連結URL。簡單地給url_for()函式傳入 一個訪問點,它返回將是一個可靠的URL地址:

1
2
3
4
5
6
@app.route('/')
def v_index():
    print url_for('v_contacts')  # /contact
    return 'see console output!'
@app.route('/contact')
def v_contacts():pass

新增查詢引數 : 使用關鍵字引數,可以在構造的URL中生成查詢串。下面的呼叫將生成 /contact?

1
2
3
4
5
6
7
format=json
@app.route('/')
def v_index():
    print url_for('v_contacts',format='json')
    return  ''
@app.route('/contact')     
def v_contacts():pass

新增URL變數 : 如果指定訪問點對應的檢視函式接收引數,那麼關鍵字引數將生成對應的引數URL。下面的 示例將生成/contact/Julia?format=html:

1
2
3
4
5
6
@app.route('/')
def v_index():
    print url_for('v_contact',name='Julia',format='html')
    return ''
@app.route('/contact/')
def v_contact(name):pass

新增錨點 :使用_anchor關鍵字可以為生成的URL新增錨點。下面的示例將生成URL /contact#part2

1
2
3
4
5
@app.route('/')
def v_index():
    print url_for('v_contacts',_anchor='part2')
@app.route('/contact')
def v_contacts():pass

外部URL : 預設情況下,url_for()生成站內URL,可以設定關鍵字引數_external 為True,生成包含站點地址的外部URL。下面的示例將生成URLhttp:///contacts:

1
2
3
4
5
@app.route('/')
def v_index():
    print url_for('v_contacts',_external=True)
@app.route('/contact')
def v_contacts():pass