Flask是一個輕量級的Web框架。雖然是輕量級的,但是對於元件一個大型的、模組化應用也是能夠實現的,“藍圖”就是這樣一種實現。對於模組化應用的實現,在Flask 0.2版本中進行了設計。本文暫時不對“藍圖”做詳細的介紹,而是先從0.2版本中的Module類的實現講起。其實,“藍圖”的實現和Module類的實現很相似。
為什麼實現模組化應用
對於大型應用而言,隨著功能的不斷增加,整個應用的規模也會擴大。按照一定的規則將應用的不同部分進行模組化,不僅能夠使整個應用邏輯清晰,也易於維護。例如,在Flask中,你也許想像如下構建一個簡單的專案:
1 2 3 4 5 6 |
/myapplication /__init__.py /views /__init__.py /admin.py /frontend.py |
以上目錄結構中,我們將之前的Flask單檔案修改成了一個應用包,所有的檢視函式都在views下,並且按照功能分為了admin和frontend兩個部分。為了實現這種模組化應用的構建,在0.2版本中Flask實現了Module類。這個類例項可以通過註冊的方式,在Flask應用建立後新增進應用。
Module類實現了一系列的方法:
- route(rule, **options)
- add_url_rule(rule, endpoint, view_func=None, **options)
- before_request(f)
- before_app_request(f)
- after_request(f)
- after_app_request(f)
- context_processor(f)
- app_context_processor(f)
- _record(func)
以上方法除了add_url_rule和_record外,都可以作為裝飾器在自己的模組中使用,這些裝飾器都返回一個函式。通過呼叫_record方法,可以將裝飾器返回的函式放到_register_events中。當Flask應用建立之後,通過執行_register_events列表中的函式,可以將這個模組註冊到應用中去。
Flask應用怎麼註冊一個Module
以下我們以一個例子來說明Flask應用怎麼註冊一個Module。
1. 專案結構
這個簡單的例子專案結構如下:
1 2 3 4 5 6 7 |
/myapplication /__init__.py /app.py /views /__init__.py /admin.py /blog.py |
admin.py和blog.py兩個模組的程式碼如下:
1 2 3 4 5 6 7 8 9 |
# admin.py from flask import Module admin = Module(__name__) @admin.route('/') def index(): return "This is admin page!" @admin.route('/profile') def profile(): return "This is profile page." |
1 2 3 4 5 6 7 8 9 |
# blog.py from flask import Module blog = Module(__name__) @blog.route('/') def index(): return "This is my blog!" @blog.route('/article/<int:id>') def article(id): return "The article id is %d." % id |
以上兩個模組中,我們首先分別建立了一個Module類,然後像寫一般的檢視函式一樣,為每個模組增加一些規則。之後,可以在建立Flask應用的時候將這些模組引入,就可以註冊了。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# app.py from flask import Flask from views.admin import admin from views.blog import blog app = Flask(__name__) @app.route('/') def index(): return "This is my app." app.register_module(blog, url_prefix='/blog') app.register_module(admin, url_prefix='/admin') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 5000, app) |
在app.py中:
- 我們首先引入了admin和blog兩個Module物件;
- 之後,我們建立了一個Flask應用app,並且為這個應用增加了一個檢視函式;
- 為了註冊模組,我們呼叫了應用的register_module方法;
- 最後,從werkzeug.serving中我們呼叫run_simple方法,用來建立一個本地的伺服器用於測試這個Flask應用。
根據以上的步驟,我們就可以測試這個應用。分別以/blog和/admin為URL字首,就可以訪問blog和admin兩個模組了。
2. 註冊Module時發生了什麼
根據上面的例子,只要簡單的呼叫Flask應用的register_module方法,就可以註冊一個Module了。關於register_module方法的程式碼如下:
1 2 3 4 5 6 7 8 9 10 |
def register_module(self, module, **options): """Registers a module with this application. The keyword argument of this function are the same as the ones for the constructor of the :class:`Module` class and will override the values of the module if provided. """ options.setdefault('url_prefix', module.url_prefix) state = _ModuleSetupState(self, **options) for func in module._register_events: func(state) |
通過以上程式碼可以發現:
- 可以通過增加url_prefix來區分不同的Module,這在app註冊admin和blog時我們已經看到了;
- 在註冊時,我們建立了一個_ModuleSetupState的類,這個類接收Flask應用和一些引數生成一個state例項。這個例項反映了當前Flask應用的狀態。
- 前面在講到Module類的時候,我們講到Module未註冊時會將自己模組的一些功能實現都放在_register_events列表中,這些功能實現都是函式形式。當需要將模組註冊到某一應用上時,只需要傳遞關於這個應用資訊的引數即可,即就是上面的state例項。這樣,通過執行函式,可以講一些屬性繫結到當前應用上去。
以上面例子中不同模組的URL繫結來講,通過註冊,應用app現形成了如下的URL“地圖”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
>>> app.url_map Map([<Rule '/admin/profile' (HEAD, GET) -> admin.profile>, <Rule '/admin/' (HEAD, GET) -> admin.index>, <Rule '/blog/' (HEAD, GET) -> blog.index>, <Rule '/' (HEAD, GET) -> index>, <Rule '/blog/article/<id>' (HEAD, GET) -> blog.article>, <Rule '/static/<filename>' (HEAD, GET) -> static>] ) >>> app.url_map._rules_by_endpoint {'admin.index': [<Rule '/admin/' (HEAD, GET) -> admin.index>], 'admin.profile': [<Rule '/admin/profile' (HEAD, GET) -> admin.profile>], 'blog.article': [<Rule '/blog/article/<id>' (HEAD, GET) -> blog.article>], 'blog.index': [<Rule '/blog/' (HEAD, GET) -> blog.index>], 'index': [<Rule '/' (HEAD, GET) -> index>], 'static': [<Rule '/static/<filename>' (HEAD, GET) -> static>] } >>> app.view_functions {'admin.index': <function views.admin.index>, 'admin.profile': <function views.admin.profile>, 'blog.article': <function views.blog.article>, 'blog.index': <function views.blog.index>, 'index': <function __main__.index> } |
這樣,就可以把不同模組的URL規則放在一起,並在endpoint和檢視函式之間形成對應關係。關於Flask應用中URL處理,可以參考:Flask應用中的URL處理。