### 導航
- [索引](# "總目錄")
- [下一頁](# "Pocoo 風格指引") |
- [上一頁](# "Flask 中的 Unicode") |
- [Flask 0.10.1 文檔](#) ?
# Flask 擴展開發
Flask,一個微框架,通常需要一些重復的步驟來讓第三方庫工作。因為在很多時候,這些步驟可以被分離出,來支持多個項目,就有了 [Flask Extension Registry](http://flask.pocoo.org/extensions/) [http://flask.pocoo.org/extensions/] 。
如果你想要為還沒有的功能創建你自己的 Flask 擴展,這份擴展開發指南會幫助你在很短的時間內讓你的應用跑起來并且感到像用戶一樣期待你的擴展運轉。
### 剖析擴展
所有的擴展都位于一個叫做 flask_something 的包,其中“ something ”是你想要連接的庫的名字。那么,例如當你計劃要為 Flask 添加一個叫做 simplexml的庫的支持時,你應該把你擴展的包命名為 flask_simplexml 。
實際的擴展名(人類可讀的名稱)無論如何會是“Flask-SimpleXML”之類的東西。確保在名字中包含“Flask”并注意大小寫。這是用戶可以在他們的 setup.py 文件中注冊你的擴展為依賴的方式。
Flask 設立了一個叫做 flask.ext 的重定向包,用戶應該從這個包導入擴展。例如,如果你有一個叫做 flask_something 的包,用戶應該用flask.ext.something 的方式導入。這樣做是為了從老命名空間的包過度。詳情見 [*擴展導入的過渡*](#) 。
但是擴展如何看起來像擴展?一個擴展必須保證它可以同時在多個 Flask 應用中工作。這是必要條件,因為許多人會使用類似 [*應用程序的工廠函數*](#) 的模式來創建應用來進行單元測試或是支持多套配置。因此,你的應用支持這種行為非常重要。
最重要的是,擴展必須與一個 setup.py 文件一起裝配,并且在 PyPI 上注冊。同樣,開發 checkout 鏈接也應該能工作,這樣才可以在 virtualenv 中容易地安裝開發版本,而不是手動下載庫。
Flask 擴展必須以 BSD 或 MIT 或更自由的許可證來許可,這樣才能被列入到 FlaskExtension Registry 。記住 Flask Extension Registry 是一個人工維護的地方,并且會視這些庫的行為來決定是否進行必要的提前審查。
### “Hello Flaskext!”
那么讓我們開始創建這樣一個 Flask 擴展。我們這里想要創建的擴展會提供 SQLite3最基礎的支持。
首先我們創建下面的目錄結構:
~~~
flask-sqlite3/
flask_sqlite3.py
LICENSE
README
~~~
這里是最重要的文件的內容:
### setup.py
下一個絕對需要的文件是 setup.py ,用于安裝你的 Flask 擴展。你可以使用下面的內容:
~~~
"""
Flask-SQLite3
-------------
This is the description for that library
"""
from setuptools import setup
setup(
name='Flask-SQLite3',
version='1.0',
url='http://example.com/flask-sqlite3/',
license='BSD',
author='Your Name',
author_email='your-email@example.com',
description='Very short description',
long_description=__doc__,
py_modules=['flask_sqlite3'],
# if you would be using a package instead use packages instead
# of py_modules:
# packages=['flask_sqlite3'],
zip_safe=False,
include_package_data=True,
platforms='any',
install_requires=[
'Flask'
],
classifiers=[
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
]
)
~~~
這有相當多的代碼,但是你實際上可以從現有的擴展中直接復制/粘貼,并修改相應的內容。
### flask_sqlite3.py
現在這個是你的擴展放代碼的位置。但是這樣一個擴展到底看起來是什么樣?最佳實踐是什么?繼續閱讀,你會有一些認識。
### 初始化擴展
許多擴展會需要某種類型的初始化步驟。比如,想象一個應用像文檔中建議的一樣([*在 Flask 中使用 SQLite 3*](#)) 正在連接到 SQLite。那么,擴展如何獲知應用對象的名稱?
相當簡單:你傳遞應用對象到它。
有兩種推薦的初始化應用的方式:
初始化函數:
> 如果你的擴展叫做 helloworld ,你應該有一個名為init_helloworld(app[,extra_args]) 的函數來為應用初始化擴展。它可以附加在處理器前/后等位置。
類:類的工作大多像初始化函數,但可以在之后進一步更改其行為。例如[OAuth 擴展](http://packages.python.org/Flask-OAuth/) [http://packages.python.org/Flask-OAuth/] 的工作方式,一個 OAuth 對象提供一些諸如OAuth.remote_app 的助手函數來創建一個使用 OAuth 的遠程應用的引用。
用什么取決于你想要什么。對于 SQLite 3 擴展,我們會使用基于類的方法,因為它提供用戶一個可以承擔打開和關閉數據庫連接的對象。
關于類,重要的是它們鼓勵在模塊層內共享。這種情況下,對象本身在任何情況下不得存儲任何應用的特定狀態,而必須可以在不同的應用間共享。
### 擴展的代碼
下面是用來復制/粘貼的 flask_sqlite3.py 的內容:
~~~
import sqlite3
from flask import current_app
# Find the stack on which we want to store the database connection.
# Starting with Flask 0.9, the _app_ctx_stack is the correct one,
# before that we need to use the _request_ctx_stack.
try:
from flask import _app_ctx_stack as stack
except ImportError:
from flask import _request_ctx_stack as stack
class SQLite3(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
# Use the newstyle teardown_appcontext if it's available,
# otherwise fall back to the request context
if hasattr(app, 'teardown_appcontext'):
app.teardown_appcontext(self.teardown)
else:
app.teardown_request(self.teardown)
def connect(self):
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
def teardown(self, exception):
ctx = stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
def connection(self):
ctx = stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
return ctx.sqlite3_db
~~~
那么這是這些代碼做的事情:
1.
__init__ 方法接受一個可選的應用對象,并且如果提供,會調用 init_app 。
1.
init_app 方法使得 SQLite3 對象不需要應用對象就可以實例化。這個方法支持工廠模式來創建應用。 init_app 會為數據庫設定配置,如果不提供配置,默認是一個內存中的數據庫。此外, init_app 方法附加了 teardown 處理器。它會試圖使用新樣式的應用上下文處理器,并且如果它不存在,退回到請求上下文處理器。
1.
接下來,我們定義了 connect 方法來打開一個數據庫連接。
1.
最后,我們添加一個 connection 屬性,首次訪問時打開數據庫連接,并把它存儲在上下文。這也是處理資源的推薦方式:在資源第一次使用時惰性獲取資源。
注意這里,我們把數據庫連接通過 _app_ctx_stack.top 附加到應用上下文的棧頂。擴展應該使用上下文的棧頂來存儲它們自己的信息,并使用足夠復雜的名稱。注意如果應用使用不支持它的老版本的 Flask 我們退回到_request_ctx_stack.top 。
那么為什么我們決定在此使用基于類的方法?因為使用我們的擴展的情況看起來會是這樣:
~~~
from flask import Flask
from flask_sqlite3 import SQLite3
app = Flask(__name__)
app.config.from_pyfile('the-config.cfg')
db = SQLite3(app)
~~~
你之后可以在視圖中這樣使用數據庫:
~~~
@app.route('/')
def show_all():
cur = db.connection.cursor()
cur.execute(...)
~~~
同樣地,如果你在請求之外,而你在使用支持應用上下文 Flask 0.9 或之后的版本,你可以用同樣的方法使用數據庫:
~~~
with app.app_context():
cur = db.connection.cursor()
cur.execute(...)
~~~
在 with 塊的最后,銷毀處理器會自動執行。
此外, init_app 方法用于支持創建應用的工廠模式:
~~~
db = Sqlite3()
# Then later on.
app = create_app('the-config.cfg')
db.init_app(app)
~~~
記住已審核的 Flask 擴展需要支持用工廠模式來創建應用(下面會解釋)。
init_app 的注意事項
如你所見, init_app 不分配 app 到 self 。這是故意的!基于類的 Flask 擴展必須只在應用傳遞到構造函數時在對象上存儲應用。這告訴擴展:我對使用多個應用沒有興趣。
當擴展需要找出當前的應用且它沒有一個指向其的引用時,必須使用[current_app](# "flask.current_app") 上下文局域變量或用一種你可以顯式傳遞應用的方法更改 API 。
### 使用 _app_ctx_stack
在上面的例子中,在每個請求之前,一個 sqlite3_db 被分配到_app_ctx_stack.top 。在一個視圖函數中,這個變量可以使用 SQLite3的屬性 connection 來訪問。在請求銷毀時, sqlite3_db 連接被關閉。通過使用這個模式, *相同* 的 sqlite3 數據庫連接在請求期間對任何需要它的東西都是可訪問的。
如果 [_app_ctx_stack](# "flask._app_ctx_stack") 因為用戶使用了老版本的 Flask 不存在,建議退化到限定在請求中的 [_request_ctx_stack](# "flask._request_ctx_stack") 。
### 銷毀行為
*這只在你想要支持 Flask 0.6 和更老版本時有關*
由于在 Flask 0.7 中關于在請求的最后運行的函數的變更,你的應用需要在此格外小心,如果要繼續支持 Flask 的更老版本。下面的模式是一個兼顧新舊的好方法:
~~~
def close_connection(response):
ctx = _request_ctx_stack.top
ctx.sqlite3_db.close()
return response
if hasattr(app, 'teardown_request'):
app.teardown_request(close_connection)
else:
app.after_request(close_connection)
~~~
嚴格地講,上面的代碼是錯誤的,因為銷毀函數接受異常且典型地不返回任何東西。盡管如此,因為返回值被丟棄,這剛好會工作,假設中間的代碼不觸碰傳遞的參數。
### 他山之石,可以攻玉
本文檔只接觸了擴展開發中絕對的最小部分,如果你想要了解更多,一個非常好的主意是查看 [Flask Extension Registry](http://flask.pocoo.org/extensions/) [http://flask.pocoo.org/extensions/] 上已有的擴展。如果你感到失落,也有[郵件列表](http://flask.pocoo.org/mailinglist/) [http://flask.pocoo.org/mailinglist/] 和 [IRC 頻道](http://flask.pocoo.org/community/irc/) [http://flask.pocoo.org/community/irc/] 來獲取一些漂亮 API 的想法。特別是當你在做之前沒人做過的東西,這會是一個非常好的主意來獲得更多投入。這不僅獲得人們會想從擴展中得到什么的想法,也可避免多個開發者重復發明輪子。
記住:良好的 API 設計是困難的,所以請在郵件列表里介紹你的項目,讓其它開發者在 API 設計上助你一臂之力。
最好的 Flask 擴展是那些為 API 共享通用風格的擴展,并且這只在起初就協作時奏效。
### 已審核的擴展
Flask 也有已審核的擴展的概念。已審核的擴展被作為 Flask 自身的一部分來測試來保證在新版本中不會破壞。這些已審核的擴展會在[Flask Extension Registry](http://flask.pocoo.org/extensions/) [http://flask.pocoo.org/extensions/] 中列出,并有相應的標記。如果你想要自己的擴展通過審核,你需要遵守下面的指導方針:
1. 一個通過審核的 Flask 擴展需要一個維護者。如果一個擴展作者想要超越項目,項目應該尋找一個新的維護者,包括完整的源碼托管過渡和 PyPI 訪問。如果沒有可用的維護者,請給 Flask 核心團隊訪問權限。
1. 一個通過審核的 Flask 擴展必須確切地提供一個名為 flask_extensioname 的包或模塊。它們也可能駐留在 flaskext 命名空間包內部,雖然現在這不被推薦。
1. 它必須伴隨一個可以使用 maketest 或 pythonsetup.pytest 的調用測試套件。對于用 maketest 測試的套件,擴展必須確保所有測試需要的依賴關系都被自動處理好。如果測試由 pythonsetup.pytest 調用,測試的依賴關系由 setup.py 文件指定。測試套件也必須是發行版的一部分。
1. 通過審核的擴展的 API 可以通過下面特性的檢查:- 一個通過審核的擴展必須支持在同一個 Python 進程中支持多個應用- 必須支持使用工廠模式創建應用
1. 必須以 BSD/MIT/WTFPL 許可
1. 官方擴展的命名模式是 *Flask-ExtensionName* 或 *ExtensionName-Flask*
1. 通過審核的擴展必須在 setup.py 文件里定義好它們的依賴關系,除非因其在 PyPI 上不可用而不能滿足這個依賴。
1. 擴展的文檔必須使用兩種 Flask 的 Sphinx 文檔主題中的一個
1. setup.py 描述(因此PyPI 描述同)必須鏈接到文檔、網站(如果有),并且必須有一個鏈接來自動安裝開發版本( PackageName==dev )
1. 安裝腳本中的 zip_safe 標志必須被設置為 False ,即使擴展對于壓縮是安全的
1. 現行擴展必須支持 Python 2.6 以及 2.7
### 擴展導入的過渡
一段時間,我們推薦對 Flask 擴展使用命名空間包。這在實踐中被證明是有問題的,因為許多不同命名空間包系統存在競爭,并且 pip 會自動在不同的系統中切換,這給用戶導致了許多問題。
現在,我們推薦命名包為 flask_foo 替代過時的 flaskext.foo 。Flask0.8 引入了重定向導入系統,允許從 flask.ext.foo 導入,并且如果flaskext.foo 失敗時,會首先嘗試 flask_foo 。
Flask 擴展應該力勸用戶從 flask.ext.foo 導入,而不是 flask_foo或 flaskext_foo ,這樣擴展可以遷移到新的包名稱而不煩擾用戶。
? 版權所有 2013, Armin Ronacher.
- 歡迎使用 Flask
- 前言
- 給有經驗程序員的前言
- 安裝
- 快速入門
- 教程
- 介紹 Flaskr
- 步驟 0: 創建文件夾
- 步驟 1: 數據庫模式
- 步驟 2: 應用設置代碼
- 步驟 3: 創建數據庫
- 步驟 4: 請求數據庫連接
- 步驟 5: 視圖函數
- 步驟 6: 模板
- 步驟 7: 添加樣式
- 福利: 應用測試
- 模板
- 測試 Flask 應用
- 記錄應用錯誤
- 配置處理
- 信號
- 即插視圖
- 應用上下文
- 請求上下文
- 用藍圖實現模塊化的應用
- Flask 擴展
- 與 Shell 共舞
- Flask 代碼模式
- 大型應用
- 應用程序的工廠函數
- 應用調度
- 使用 URL 處理器
- 部署和分發
- 使用 Fabric 部署
- 在 Flask 中使用 SQLite 3
- 在 Flask 中使用 SQLAlchemy
- 上傳文件
- 緩存
- 視圖裝飾器
- 使用 WTForms 進行表單驗證
- 模板繼承
- 消息閃現
- 用 jQuery 實現 Ajax
- 自定義錯誤頁面
- 延遲加載視圖
- 在 Flask 中使用 MongoKit
- 添加 Favicon
- 數據流
- 延遲請求回調
- 添加 HTTP Method Overrides
- 請求內容校驗碼
- 基于 Celery 的后臺任務
- 部署選擇
- mod_wsgi (Apache)
- 獨立 WSGI 容器
- uWSGI
- FastCGI
- CGI
- 聚沙成塔
- API
- JSON 支持
- Flask 中的設計決策
- HTML/XHTML 常見問題
- 安全注意事項
- Flask 中的 Unicode
- Flask 擴展開發
- Pocoo 風格指引
- Python 3 支持
- 升級到最新版本
- Flask Changelog
- 許可證
- 術語表