Flask扩展开发¶
Flask是一个微框架,通常需要一些重复的步骤才能让第三方库工作。许多这样的扩展已经在 PyPI .
如果你需要创建自己的扩展,那么本文可以帮助你让扩展立马上线运行。
剖析一个扩展¶
扩展都放在一个名如 flask_something 的包中。其中的“ something ”就是扩 展所要连接的库的名称。例如假设你要为 Flask 添加 simplexml 库的支持,那么 扩展的包名称就应该是 flask_simplexml 。
但是,真正扩展的名称(可读名称)应当形如“ Flask-SimpleXML ”。请确保名称 中包含“ Flask ”,并且注意大小写。这样用户就可以在他们的 setup.py 文件中注册依赖。
但是扩展本身是什么样子的?扩展必须确保它同时与多个flask应用程序实例一起工作。这是一项要求,因为许多人将使用类似 应用工厂 模式来根据需要创建它们的应用程序,以帮助单元测试并支持多种配置。因此,应用程序支持这种行为是至关重要的。
最重要的是,扩展必须与一个 setup.py 文件一起分发,并且在 PyPI 上注 册。同时,用于开发的检出链接也应该能工作,以便于在 virtualenv 中安装开发版 本,而不是手动下载库。
Flask 扩展必须使用 BSD 或 MIT 或更自由的许可证来许可,这样才能被添加进 Flask 扩展注册表。请记住, Flask 扩展注册表是比较稳健的,并且扩展在发布前 会进行预审是否符合要求。
“ 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¶
现在这就是您的扩展代码所在的位置。但是这样的扩展到底应该是什么样的呢?最佳实践是什么?继续阅读以获得一些见解。
初始化扩展¶
许多扩展需要某种初始化步骤。例如,假设一个应用程序正在连接到SQLite,如文档所示 (将sqlite 3与Flask一起使用 ). 那么扩展如何知道应用程序对象的名称呢?
相当简单:你把名称传递给扩展。
建议使用两种方法初始化扩展:
初始化函数:
如果你的扩展名为 helloworld ,那么你可能有一个名为 init_helloworld(app[, extra_args]) 的函数。该函数用来为应用初始化 扩展,它可以在处理器之前或之后。
初始化类:
初始化类与初始化函数的工作方式大致相同,区别是类在以后可以进一步改动。 例如,查看一下 OAuth 扩展 的工作方式:有一个 OAuth 对象提供一些 辅助函数(比如 OAuth.remote_app )来创建使用 OAuth 的远程应用的引用。
使用哪种方式取决于你。对于 SQLite 3 扩展,我们会使用基于类的方式,因为这样 可以提供给用户一个用于打开和关闭数据库连接的对象。
当设计类时,重要的一点是使用它们在模块层易于复用。也就是说,对象本身在任何 情况下不应存储任何应用的特定状态,而必须可以在不同的应用之间共享。
扩展代码¶
以下是 flask_sqlite3.py 复制/粘贴:
import sqlite3
from flask import current_app, _app_ctx_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:')
app.teardown_appcontext(self.teardown)
def connect(self):
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
def teardown(self, exception):
ctx = _app_ctx_stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
def connection(self):
ctx = _app_ctx_stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
return ctx.sqlite3_db
那么这是这些代码的含义是什么:
__init__ 方法接收应用对象,该对象是可选的。如果提供了该对象,那么 就调用 init_app 。
这个init_app使得 SQLite3不需要应用对象就可以实例化。因为它支持工厂模式来创建应用。init_app 会配置数据库,如果不提供配置,默认配置则为内存数据库。此外, init_app 附加了 teardown 处理器。
接下来,我们定义
connect
打开数据库连接的方法。最后,我们添加一个 connection 属性,首次访问时打开数据库连接,并把 它存储在环境中。这也是处理资源的推荐方式:在资源第一次使用时获取资源, 即惰性获取。
注意这里,我们把数据库连接通过 _app_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(...)
同样,如果在请求之外,可以通过压入应用情境的方法使用数据库:
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 环境局部变量或用一种你可以显式传递应用的方法 更改 API 。
使用应用程序CTX堆栈¶
在上面的例子中,在每个请求之前,一个 sqlite3_db 变量被分配到 _app_ctx_stack.top 。在一个视图函数中,这个变量可以使用 SQLite3 的属性 connection 来访问。在请求解散时, sqlite3_db 连接被关闭。 通过使用这个模式,在请求持续的期间,可以访问 相同 的 sqlite3 数据库连接。
学习借鉴¶
本文档仅涉及扩展开发的最低限度。如果您想了解更多信息,最好查看 PyPI . 如果你觉得迷路了,还有 mailinglist 以及 Discord server 为好看的API提供一些建议。尤其是如果你在做之前没人做过什么事情,那么得到更多的信息可能是一个很好的主意。这不仅可以从扩展中产生有用的反馈,还可以避免让多个开发人员单独处理几乎相同的问题。
记住:好的API设计是很难的,所以在邮件列表中介绍您的项目,让其他开发人员帮助您设计API。
最好的 Flask 扩展是那些共享 API 智慧的扩展,因此越早共享越有效。
已审核的扩展¶
Flask之前有过批准扩展的概念。这些都经过了一些支持和兼容性的审查。虽然随着时间的推移,这个列表变得太难维护了,但是这些指南仍然与今天维护和开发的所有扩展相关,因为它们帮助烧瓶生态系统保持一致性和兼容性。
批准的烧瓶扩展需要维护人员。如果一个扩展作者想跳出项目之外,项目应该找到一个新的维护者,并转移对存储库、文档、PyPI和任何其他服务的访问权。如果没有维修人员,请联系托盘核心团队。
命名方案是 Flask-ExtensionName 或 ExtensionName-Flask . 它必须只提供一个名为
flask_extension_name
.扩展必须是BSD或MIT授权的。它必须是开源的并且是公开的。
扩展的API必须具有以下特性:
它必须支持在同一个Python进程中运行的多个应用程序。使用
current_app
而不是self.app
,存储每个应用程序实例的配置和状态。必须能够使用工厂模式来创建应用程序。使用
ext.init_app()
模式。
从存储库的克隆中,必须可以安装具有其依赖项的扩展
pip install -e .
.它必须提供一个可以调用的测试套件
tox -e py
或pytest
. 如果不使用tox
,测试依赖项应在requirements.txt
文件。测试必须是sdist分布的一部分。文档必须使用
flask
主题来自 Official Pallets Themes . 到文档或项目网站的链接必须在PyPI元数据或自述文件中。为了获得最大的兼容性,扩展应该支持Flask支持的相同版本的Python。到2020年,建议使用3.6+。使用
python_requires=">= 3.6"
在里面setup.py
指示支持的版本。