Flask扩展开发¶
扩展是为FlaskTM应用程序添加功能的额外程序包。而当 PyPI 包含许多烧瓶扩展,您可能找不到一个符合您的需要。如果是这种情况,您可以创建自己的文档,并将其发布以供他人使用。
本指南将展示如何创建一个FlASK扩展,以及涉及到的一些常见模式和要求。因为扩展可以做任何事情,所以本指南不能涵盖所有的可能性。
了解扩展的最好方法是查看您使用的其他扩展是如何编写的,并与其他人讨论。在我们的网站上与其他人讨论您的设计理念 Discord Chat 或 GitHub Discussions 。
最好的扩展都有共同的模式,因此熟悉使用一个扩展的任何人都不会觉得完全迷失了另一个扩展。这只有在合作进行得早的情况下才能奏效。
命名¶
烧瓶扩展通常具有 flask
在其名称中作为前缀或后缀。如果它包装了另一个库,它还应该包括库名。这使得搜索扩展名变得很容易,并且使它们的目的更加明确。
一般的Python打包建议是包索引中的安装名称和中使用的名称 import
陈述应该是相关的。导入名称为小写,单词之间用下划线分隔 (_
)。安装名称为小写或标题大小写,单词之间用短划线分隔 (-
)。如果它包装了另一个库,则更喜欢使用与该库名称相同的大小写。
以下是一些安装和导入名称示例:
Flask-Name
imported asflask_name
flask-name-lower
imported asflask_name_lower
Flask-ComboName
imported asflask_comboname
Name-Flask
imported asname_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
Dict可用于存储对应用程序上的扩展的引用,或存储特定于应用程序的某些其他状态。请注意,这是一个单一的名称空间,因此请使用您的扩展名唯一的名称,例如不带“flask”前缀的扩展名。
添加行为¶
扩展可以通过多种方式添加行为。上可用的任何设置方法 Flask
对象可以在扩展的 init_app
方法。
一种常见的模式是使用 before_request()
要在每个请求开始时初始化一些数据或连接,则 teardown_request()
在最后把它清理干净。可以将其存储在 g
,下面将讨论更多内容。
一种更懒惰的方法是提供一个初始化和缓存数据或连接的方法。例如,一个 ext.get_db
方法可以在第一次调用时创建数据库连接,因此不使用数据库的视图不会创建连接。
除了在每个视图之前和之后执行一些操作外,您的扩展可能还希望添加一些特定的视图。在这种情况下,您可以定义一个 Blueprint
,然后呼叫 register_blueprint()
在.期间 init_app
将蓝图添加到应用程序中。
配置技术¶
一个分机可以有多个级别和来源的配置。您应该考虑扩展的哪些部分属于每个扩展。
每个应用程序实例的配置,通过
app.config
价值观。对于应用程序的每次部署,这一配置可能会合理地更改。一个常见的例子是指向外部资源(如数据库)的URL。配置密钥应该以扩展名开头,这样它们就不会干扰其他扩展名。每个扩展实例的配置,通过
__init__
争论。这种配置通常会影响扩展的使用方式,因此每次部署更改它都没有意义。通过实例属性和修饰符方法为每个扩展实例进行配置。分配给它可能更符合人体工程学
ext.value
,或使用@ext.register
在创建扩展实例之后注册函数的修饰符。通过类属性进行全局配置。更改类属性,如
Ext.connection_class
无需生成子类即可自定义默认行为。这可以结合每个分机配置来覆盖默认设置。子类化和重写方法和属性。使扩展本身的API成为可以覆盖的东西,这为高级定制提供了一个非常强大的工具。
这个 Flask
对象本身使用所有这些技术。
根据您的需要和想要支持的内容,您可以自行决定适合您的扩展的配置。
在应用程序设置阶段完成且服务器开始处理请求后,不应更改配置。配置是全局的,对其所做的任何更改都不能保证对其他工作进程可见。
请求期间的数据¶
在编写FlaskTM应用程序时, g
对象用于在请求期间存储信息。例如, tutorial 将与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
与FlaskSQLAlChemy配合使用的扩展,以提供 Post
用于写和读帖子的模型和视图。
这个 Post
模型需要将Flask子类化为SQLAlChemy db.Model
对象,但这仅在创建了该扩展的实例后才可用,而不是在扩展定义其视图时可用。那么,在模型存在之前定义的视图代码如何访问模型呢?
一种方法可以是使用 基于类的视图 。在.期间 __init__
,创建模型,然后通过将模型传递给view类的 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
从上面看。将扩展名添加到 app.extensions
在……里面 init_app
,然后访问 current_app.extensions["simple_blog"].post_model
从视图。
您可能还想提供基类,以便用户可以提供他们自己的 Post
符合您的扩展期望的API的模型。这样他们就可以实施 class Post(blog.BasePost)
,然后将其设置为 blog.post_model
。
正如您所看到的,这可能会变得有点复杂。不幸的是,这里没有完美的解决方案,只有不同的策略和权衡,这取决于您的需求和您想要提供的定制程度。幸运的是,这种资源依赖并不是大多数扩展的常见需求。请记住,如果您在设计方面需要帮助,请联系我们的 Discord Chat 或 GitHub Discussions 。
推荐的扩展指南¶
FlASK以前有“批准的扩展”的概念,即在列出扩展之前,Flask维护人员对扩展的质量、支持和兼容性进行评估。虽然随着时间的推移,该列表变得太难维护,但这些指导方针仍然适用于今天维护和开发的所有扩展,因为它们帮助Flask生态系统保持一致和兼容。
扩展需要维护人员。如果扩展作者想要跳出项目,项目应该找到新的维护者,并将访问转移到存储库、文档、PyPI和任何其他服务。这个 Pallets-Eco GitHub上的组织允许在托盘维护人员的监督下进行社区维护。
命名方案是 Flask-ExtensionName 或 ExtensionName-Flask . 它必须只提供一个名为
flask_extension_name
.该扩展必须使用开源许可证。Python Web生态系统倾向于使用BSD或MIT。它必须是开放源码的,并且可以公开使用。
扩展的API必须具有以下特性:
它必须支持在同一个Python进程中运行的多个应用程序。使用
current_app
而不是self.app
,存储每个应用程序实例的配置和状态。必须能够使用工厂模式来创建应用程序。使用
ext.init_app()
模式。
从存储库的克隆中,扩展及其依赖项必须可以在可编辑模式下安装
pip install -e .
。它必须提供可通过常见工具调用的测试
tox -e py
,nox -s test
或pytest
。如果不使用tox
,测试依赖项应该在需求文件中指定。测试必须是sdist分发的一部分。指向文档或项目网站的链接必须位于PyPI元数据或自述文件中。文档应使用来自 Official Pallets Themes 。
扩展的依赖关系不应该使用上限或假设任何特定的版本方案,而应该使用下限来指示最低兼容性支持。例如,
sqlalchemy>=1.4
。使用指示受支持的Python版本
python_requires=">=version"
。自2023年4月起,FASK本身支持Python>=3.8,但这将随着时间的推移而更新。