Flask 擴充套件開發¶
擴充套件是為 Flask 應用程式新增功能的額外套件。雖然 PyPI 包含許多 Flask 擴充套件,您可能找不到符合您需求的套件。如果這種情況發生,您可以建立自己的擴充套件,並發布供其他人使用。
本指南將說明如何建立 Flask 擴充套件,以及其中涉及的一些常見模式和需求。由於擴充套件可以執行任何操作,因此本指南無法涵蓋所有可能性。
了解擴充套件的最佳方法是查看您使用的其他擴充套件的撰寫方式,並與其他人討論。在我們的 Discord 聊天室 或 GitHub 討論區 上與其他人討論您的設計想法。
最佳的擴充套件會分享常見的模式,以便任何熟悉使用一個擴充套件的人都不會對另一個擴充套件感到完全陌生。只有在早期進行協作,這才有可能實現。
命名¶
Flask 擴充套件通常在其名稱中帶有 flask
作為前綴或後綴。如果它包裝了另一個函式庫,則也應包含該函式庫的名稱。這使得搜尋擴充套件變得容易,並使其用途更清晰。
一個通用的 Python 套件建議是,來自套件索引的安裝名稱和 import
陳述式中使用的名稱應相關聯。匯入名稱是小寫,單字之間用底線 (_
) 分隔。安裝名稱是小寫或詞首大寫,單字之間用破折號 (-
) 分隔。如果它包裝了另一個函式庫,則優先使用與該函式庫名稱相同的大小寫。
以下是一些安裝和匯入名稱的範例
Flask-Name
匯入為flask_name
flask-name-lower
匯入為flask_name_lower
Flask-ComboName
匯入為flask_comboname
Name-Flask
匯入為name_flask
擴充套件類別和初始化¶
所有擴充套件都需要一個進入點,用於初始化應用程式的擴充套件。最常見的模式是建立一個類別,表示擴充套件的組態和行為,並具有一個 init_app
方法,將擴充套件實例應用於給定的應用程式實例。
class HelloExtension:
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self, app):
app.before_request(...)
重要的是,應用程式不應儲存在擴充套件上,不要執行 self.app = app
。擴充套件應直接存取應用程式的唯一時間是在 init_app
期間,否則應使用 current_app
。
這允許擴充套件支援應用程式工廠模式,避免在使用者程式碼中的其他位置匯入擴充套件實例時出現循環匯入問題,並使使用不同組態進行測試更容易。
hello = HelloExtension()
def create_app():
app = Flask(__name__)
hello.init_app(app)
return app
在上面,hello
擴充套件實例獨立於應用程式存在。這表示使用者專案中的其他模組可以執行 from project import hello
,並在應用程式存在之前在藍圖中使用擴充套件。
Flask.extensions
字典可用於在應用程式上儲存對擴充套件的參考,或特定於應用程式的其他狀態。請注意,這是一個單一命名空間,因此請使用對您的擴充套件而言唯一的名稱,例如不帶 "flask" 前綴的擴充套件名稱。
新增行為¶
擴充套件可以透過多種方式新增行為。Flask
物件上可用的任何設定方法都可以在擴充套件的 init_app
方法期間使用。
一個常見的模式是使用 before_request()
在每個請求開始時初始化一些資料或連線,然後使用 teardown_request()
在結束時清理它。這可以儲存在 g
上,稍後將詳細討論。
更懶惰的方法是提供一個初始化並快取資料或連線的方法。例如,ext.get_db
方法可以在第一次呼叫時建立資料庫連線,以便不使用資料庫的視圖不會建立連線。
除了在每個視圖之前和之後執行某些操作外,您的擴充套件可能也想要新增一些特定的視圖。在這種情況下,您可以定義一個 Blueprint
,然後在 init_app
期間呼叫 register_blueprint()
,以將藍圖新增到應用程式。
組態技巧¶
擴充套件可以有多個層級和來源的組態。您應該考慮擴充套件的哪些部分屬於每個層級和來源。
每個應用程式實例的組態,透過
app.config
值。這是對於應用程式的每個部署都可能合理變更的組態。一個常見的範例是外部資源的 URL,例如資料庫。組態金鑰應以擴充套件的名稱開頭,以便它們不會干擾其他擴充套件。每個擴充套件實例的組態,透過
__init__
引數。此組態通常會影響擴充套件的使用方式,使其在每次部署時變更它沒有意義。每個擴充套件實例的組態,透過實例屬性和裝飾器方法。在建立擴充套件實例後,指派給
ext.value
,或使用@ext.register
裝飾器註冊函式,可能會更符合人體工學。透過類別屬性的全域組態。變更類別屬性(如
Ext.connection_class
)可以自訂預設行為,而無需建立子類別。這可以與每個擴充套件的組態結合,以覆寫預設值。子類別化和覆寫方法和屬性。使擴充套件本身的 API 成為可以覆寫的內容,為進階自訂提供了非常強大的工具。
Flask
物件本身使用所有這些技巧。
根據您的需求和您想要支援的內容,由您決定哪些組態適合您的擴充套件。
組態不應在應用程式設定階段完成且伺服器開始處理請求後變更。組態是全域的,對其進行的任何變更都不能保證對其他工作程序可見。
請求期間的資料¶
在撰寫 Flask 應用程式時,g
物件用於在請求期間儲存資訊。例如,教學 將與 SQLite 資料庫的連線儲存為 g.db
。擴充套件也可以使用此物件,但需要謹慎。由於 g
是一個單一的全域命名空間,因此擴充套件必須使用唯一的名稱,這些名稱不會與使用者資料衝突。例如,使用擴充套件名稱作為前綴或作為命名空間。
# an internal prefix with the extension name
g._hello_user_id = 2
# or an internal prefix as a namespace
from types import SimpleNamespace
g._hello = SimpleNamespace()
g._hello.user_id = 2
g
中的資料會在應用程式內容中持續存在。當請求內容處於活動狀態時,或當 CLI 命令執行時,應用程式內容處於活動狀態。如果您要儲存應關閉的內容,請使用 teardown_appcontext()
以確保在應用程式內容結束時關閉它。如果它應僅在請求期間有效,或者在請求之外的 CLI 中不會使用,請使用 teardown_request()
。
視圖和模型¶
您的擴充套件視圖可能想要與資料庫中的特定模型互動,或與連接到應用程式的其他擴充套件或資料互動。例如,讓我們考慮一個 Flask-SimpleBlog
擴充套件,它與 Flask-SQLAlchemy 搭配使用,以提供 Post
模型和視圖來撰寫和讀取文章。
Post
模型需要子類別化 Flask-SQLAlchemy db.Model
物件,但只有在您建立該擴充套件的實例後,才可以使用該物件,而不是在您的擴充套件定義其視圖時。那麼,在模型存在之前定義的視圖程式碼如何存取模型?
一種方法是使用 基於類別的視圖。在 __init__
期間,建立模型,然後透過將模型傳遞給視圖類別的 as_view()
方法來建立視圖。
class PostAPI(MethodView):
def __init__(self, model):
self.model = model
def get(self, id):
post = self.model.query.get(id)
return jsonify(post.to_json())
class BlogExtension:
def __init__(self, db):
class Post(db.Model):
id = db.Column(primary_key=True)
title = db.Column(db.String, nullable=False)
self.post_model = Post
def init_app(self, app):
api_view = PostAPI.as_view(model=self.post_model)
db = SQLAlchemy()
blog = BlogExtension(db)
db.init_app(app)
blog.init_app(app)
另一種技巧是使用擴充套件上的屬性,例如上面的 self.post_model
。在 init_app
中將擴充套件新增到 app.extensions
,然後從視圖存取 current_app.extensions["simple_blog"].post_model
。
您可能還想要提供基底類別,以便使用者可以提供自己的 Post
模型,該模型符合您的擴充套件期望的 API。因此,他們可以實作 class Post(blog.BasePost)
,然後將其設定為 blog.post_model
。
如您所見,這可能會變得有點複雜。不幸的是,這裡沒有完美的解決方案,只有不同的策略和權衡,具體取決於您的需求以及您想要提供的自訂程度。幸運的是,這種資源依賴性並不是大多數擴充套件的常見需求。請記住,如果您需要設計方面的幫助,請在我們的 Discord 聊天室 或 GitHub 討論區 上提問。
建議的擴充套件指南¶
Flask 之前有「核准的擴充套件」的概念,其中 Flask 維護人員在列出擴充套件之前評估了擴充套件的品質、支援和相容性。雖然該列表隨著時間的推移變得難以維護,但這些指南對於今天維護和開發的所有擴充套件仍然相關,因為它們有助於 Flask 生態系統保持一致和相容。
擴充套件需要維護人員。如果擴充套件作者想要超越專案,則專案應找到新的維護人員並轉讓對儲存庫、文件、PyPI 和任何其他服務的存取權。Pallets-Eco GitHub 組織允許在 Pallets 維護人員的監督下進行社群維護。
命名方案為Flask-ExtensionName 或 ExtensionName-Flask。它必須僅提供一個名為
flask_extension_name
的套件或模組。擴充套件必須使用開放原始碼授權。Python 網路生態系統傾向於偏好 BSD 或 MIT。它必須是開放原始碼且公開可用。
擴充套件的 API 必須具有以下特性
它必須支援在同一個 Python 程序中執行的多個應用程式。使用
current_app
而不是self.app
,每個應用程式實例儲存組態和狀態。必須可以使用工廠模式來建立應用程式。使用
ext.init_app()
模式。
從儲存庫的克隆版本開始,必須可以使用
pip install -e .
在可編輯模式下安裝具有其相依性的擴充套件。它必須附帶可以使用常見工具(如
tox -e py
、nox -s test
或pytest
)調用的測試。如果未使用tox
,則測試相依性應在需求檔案中指定。測試必須是 sdist 發行版的一部分。PyPI metadata 或 readme 中必須包含文件或專案網站的連結。文件應使用 官方 Pallets 主題 中的 Flask 主題。
擴充套件的相依性不應使用上限或假設任何特定的版本方案,而應使用下限來指示最小相容性支援。例如,
sqlalchemy>=1.4
。使用
python_requires=">=version"
指示支援的 Python 版本。截至 2024 年 10 月,Flask 本身支援 Python >=3.9,並且這將隨著時間的推移而更新。