Flask 扩展开发 — Flask 文档

来自菜鸟教程
Flask/docs/1.1.x/extensiondev
跳转至:导航、​搜索

Flask 扩展开发

Flask 作为一个微框架,通常需要一些重复的步骤才能让第三方库正常工作。 许多这样的扩展已经在 PyPI 上可用。

如果您想为尚不存在的东西创建自己的 Flask 扩展,本扩展开发指南将帮助您立即运行您的扩展,并感觉用户希望您的扩展能够正常运行。

扩展的剖析

扩展都位于一个名为 flask_something 的包中,其中“something”是您要桥接的库的名称。 因此,例如,如果您计划向 Flask 添加对名为 simplexml 的库的支持,您可以将扩展包命名为 flask_simplexml

然而,实际扩展的名称(人类可读的名称)将类似于“Flask-SimpleXML”。 确保在该名称的某处包含名称“Flask”并检查大小写。 这就是用户如何在他们的 setup.py 文件中注册对你的扩展的依赖。

但是扩展本身是什么样子的呢? 扩展必须确保它同时与多个 Flask 应用程序实例一起使用。 这是一项要求,因为许多人会使用 应用程序工厂 模式之类的模式来根据需要创建他们的应用程序,以帮助进行单元测试并支持多种配置。 因此,您的应用程序支持这种行为至关重要。

最重要的是,扩展必须附带一个 setup.py 文件并在 PyPI 上注册。 此外,开发结帐链接应该有效,以便人们可以轻松地将开发版本安装到他们的 virtualenv 中,而无需手动下载库。

Flask 扩展必须在 BSD、MIT 或更自由的许可下获得许可,才能在 Flask 扩展注册表中列出。 请记住,Flask 扩展注册表是一个经过审核的地方,如果库的行为符合要求,它们将被预先审查。


“你好 Flaskext!”

所以让我们开始创建这样一个 Flask 扩展。 我们要在这里创建的扩展将为 SQLite3 提供非常基本的支持。

首先,我们创建以下文件夹结构:

flask-sqlite3/
    flask_sqlite3.py
    LICENSE
    README

以下是最重要文件的内容:

设置文件

下一个绝对需要的文件是用于安装 Flask 扩展的 setup.py 文件。 您可以使用以下内容:

"""
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 的应用程序,如文档所建议的(Using SQLite 3 with 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

下面是这些代码行的作用:

  1. __init__ 方法接受一个可选的应用程序对象,如果提供,将调用 init_app

  2. init_app 方法的存在使得 SQLite3 对象可以在不需要应用程序对象的情况下被实例化。 此方法支持用于创建应用程序的工厂模式。 init_app 将设置数据库的配置,如果没有提供配置,则默认为内存数据库。 此外,init_app 方法附加了 teardown 处理程序。

  3. 接下来,我们定义一个 connect 方法来打开一个数据库连接。

  4. 最后,我们添加一个 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)

请记住,已批准的烧瓶扩展需要支持这种用于创建应用程序的工厂模式(如下所述)。

关于init_app的注意事项

如您所见,init_app 不会将 app 分配给 self。 这是故意的! 当应用程序被传递给构造函数时,基于类的 Flask 扩展必须只将应用程序存储在对象上。 这告诉扩展:我对使用多个应用程序不感兴趣。

当扩展需要查找当前应用程序并且它没有对它的引用时,它必须使用 current_app 本地上下文或以可以显式传递应用程序的方式更改 API。


使用 _app_ctx_stack

在上面的例子中,在每个请求之前,一个 sqlite3_db 变量被分配给 _app_ctx_stack.top。 在视图函数中,可以使用 SQLite3connection 属性访问此变量。 在请求拆除期间,sqlite3_db 连接关闭。 通过使用这种模式,在请求期间任何需要它的人都可以访问到 sqlite3 数据库的 same 连接。


向他人学习

本文档仅涉及扩展开发的最低限度。 如果您想了解更多信息,最好查看 PyPI 上的现有扩展。 如果您感到迷茫,仍然可以使用 mailinglistDiscord 服务器 来获得一些漂亮 API 的想法。 特别是如果你做了一些比你还早的事情,那么获得更多的投入可能是一个很好的主意。 这不仅会产生关于人们可能希望从扩展中获得什么的有用反馈,而且还避免让多个开发人员孤立地处理几乎相同的问题。

记住:好的 API 设计很难,所以在邮件列表中介绍你的项目,让其他开发人员帮助你设计 API。

最好的 Flask 扩展是共享 API 通用习语的扩展。 而这只有在协作发生得早的情况下才能奏效。


批准的扩展

Flask 以前有批准扩展的概念。 这些都伴随着一些支持和兼容性的审查。 虽然这个列表随着时间的推移变得难以维护,但这些指南仍然与今天维护和开发的所有扩展相关,因为它们帮助 Flask 生态系统保持一致和兼容。

  1. 经批准的 Flask 扩展需要维护者。 如果扩展作者想要超越项目,项目应该找到一个新的维护者并转移对存储库、文档、PyPI 和任何其他服务的访问权限。 如果没有可用的维护者,请访问 Pallets 核心团队。

  2. 命名方案是 Flask-ExtensionNameExtensionName-Flask。 它必须提供一个名为 flask_extension_name 的包或模块。

  3. 扩展必须是 BSD 或 MIT 许可的。 它必须是开源的并且是公开可用的。

  4. 扩展的 API 必须具有以下特征:

    • 它必须支持在同一个 Python 进程中运行的多个应用程序。 使用 current_app 而不是 self.app,存储每个应用程序实例的配置和状态。

    • 必须可以使用工厂模式来创建应用程序。 使用 ext.init_app() 模式。

  5. 从存储库的克隆中,扩展及其依赖项必须可通过 pip install -e . 安装。

  6. 它必须提供一个可以用 tox -e pypytest 调用的测试套件。 如果不使用 tox,则应在 requirements.txt 文件中指定测试依赖项。 测试必须是 sdist 分布的一部分。

  7. 文档必须使用来自 Official Pallets Themesflask 主题。 指向文档或项目网站的链接必须在 PyPI 元数据或自述文件中。

  8. 为了获得最大的兼容性,扩展应该支持 Flask 支持的相同版本的 Python。 自 2020 年起建议使用 3.6+。 在 setup.py 中使用 python_requires=">= 3.6" 表示支持的版本。