ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flask 扩展(Extensions)完全指南:查找、使用与构建自己的 Flask 扩展

Flask 扩展(Extensions)完全指南:查找、使用与构建自己的 Flask 扩展 Flask 扩展Extensions完全指南查找、使用与构建自己的 Flask 扩展【免费下载链接】flaskThe Python micro framework for building web applications.项目地址: https://gitcode.com/gh_mirrors/fl/flaskFlask 是一个轻量级的 Python Web 微框架其核心保持精简而丰富的能力来自庞大的扩展Extension生态。本指南以 Flask 官方文档 docs/extensions.rst 为主体系统讲解 Flask 扩展的查找方式、标准使用范式并深入扩展开发的最佳实践扩展类设计、init_app初始化模式、app.extensions状态存储、请求生命周期钩子、配置分层、g对象使用规范以及社区推荐的扩展发布准则。读完本文你将能够熟练选用现成扩展并开发、发布一个符合 Flask 生态惯例的高质量扩展。什么是 Flask 扩展扩展Extensions是为 Flask 应用添加功能的额外包。例如一个扩展可能为应用添加发送邮件的能力或连接数据库的能力有些扩展甚至带来一整套新框架用于构建特定类型的应用例如 REST API。这种小而精的核心 可插拔的扩展生态是 Flask 的设计哲学。应用通过pip install安装扩展在代码中导入并初始化即可获得相应能力而无需把核心框架做得臃肿。查找扩展Flask 扩展通常命名为Flask-Foo或Foo-Flask例如Flask-SQLAlchemy、Flask-Mail、Flask-WTF等。你可以在 PyPI 上搜索带有Framework :: Flask标签的包来发现扩展。从源码可以看到Flask 官方在 pyproject.toml 的classifiers中也维护了这一约定Framework :: Flask这是 Flask 生态统一可搜索性的基础开发者按统一命名与标签发布使用者按统一方式检索。使用扩展标准初始化模式每个扩展都有自己的文档安装、配置与使用方式以各自文档为准。不过大多数扩展遵循一套通用惯例扩展从app.config读取自己的配置并在初始化时接收一个应用实例。例如一个名为 Flask-Foo 的扩展可能这样使用from flask_foo import Foo foo Foo() app Flask(__name__) app.config.update( FOO_BARbaz, FOO_SPAMeggs, ) foo.init_app(app)这段代码展示了两阶段初始化two-phase initialization创建扩展实例foo Foo()时扩展尚不绑定任何应用可先配置扩展自身的构造参数初始化应用foo.init_app(app)将扩展挂载到具体应用实例上此时扩展从app.config中读取FOO_BAR、FOO_SPAM等以扩展名称为前缀的配置项。为什么不在Foo(app)里一次性完成因为两阶段模式支持应用工厂Application Factory模式应用对象可以在运行时按需创建扩展实例却可以模块级提前创建并被多个模块共享。构建自己的扩展虽然 PyPI 上已有大量 Flask 扩展但你未必能找到恰好满足需求的。这时你可以自己动手开发一个并发布给他人使用。开发前请先阅读 Flask 官方的扩展开发指南 docs/extensiondev.rst本节内容即围绕该指南展开。学习扩展开发的最好方式是研究你正在使用的扩展是怎么写的并积极参与社区讨论。命名规范一个 Flask 扩展通常以flask作为名称的前缀或后缀如果它封装了另一个库还应包含该库的名字这样便于搜索且意图清晰。Python 打包的一般建议是包索引中的安装名install name与import语句中的导入名import name应保持关联。导入名全小写、单词间用下划线_分隔安装名可以是小写或首字母大写、单词间用连字符-分隔如果封装了其他库则优先沿用该库命名的大小写风格。一些安装名与导入名的示例安装名PyPI导入名importFlask-Nameflask_nameflask-name-lowerflask_name_lowerFlask-ComboNameflask_combonameName-Flaskname_flask扩展类与init_app所有扩展都需要一个入口来把扩展初始化到应用上。最常见的模式是创建一个代表扩展配置与行为的类提供init_app方法把扩展实例应用到给定的应用实例上。class HelloExtension: def __init__(self, appNone): 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并在应用被创建之前就在蓝图中使用该扩展。不存储self.app带来的三个好处这也是 Flask 官方指南明确列出的支持应用工厂模式同一个扩展实例可以被多个应用共享初始化避免循环导入问题用户代码在其他模块导入扩展实例时不会因为绑定了某个应用而产生循环依赖更易测试可以用不同配置轻松创建多个应用来测试扩展。从 Flask 源码可以印证current_app的设计它定义在 src/flask/globals.py 中是基于contextvars.ContextVar的LocalProxy由_cv_app携带当前应用上下文在应用上下文之外访问它会抛出 Working outside of application context 错误并提示使用with app.app_context()。扩展在视图、CLI 命令或with app.app_context():块中都可以安全地通过current_app获取当前应用而不必也不应持有对应用的长期引用。在app.extensions上存储扩展状态Flask.extensions字典是官方为扩展预留的应用特定状态存放处例如数据库引擎等。它的定义位于 src/flask/sansio/app.py#: a place where extensions can store application specific state. For #: example this is where an extension could store database engines and #: similar things. #: #: The key must match the name of the extension module. For example in #: case of a Flask-Foo extension in flask_foo, the key would be #: foo. self.extensions: dict[str, t.Any] {}需要注意这是一个单一命名空间所有扩展共享。因此 key 必须使用你扩展独有的名称例如去掉 flask 前缀后的扩展名。约定是key 与扩展模块名对应以Flask-Foo模块flask_foo为例key 应为foo。为扩展添加行为扩展添加行为的方式有很多。任何Flask对象上可用的 setup 方法都可以在扩展的init_app中调用。请求前后钩子最常用一种常见模式是使用before_request在每个请求开始时初始化某些数据或连接再使用teardown_request在请求结束时清理数据可存放在g对象上下文详述。def init_app(self, app): app.before_request(self._open_connection) app.teardown_request(self._close_connection)从 Flask 源码 src/flask/sansio/scaffold.py 可以看到before_request是一个setupmethod把函数注册进before_request_funcs字典key 为None表示应用于所有请求teardown_request同理注册到teardown_request函数列表。teardown_request的文档特别提醒它用于必须执行的清理如关闭资源因为after_request在出错时可能不会全部执行。懒加载模式更懒惰的做法是提供一个在首次调用时才初始化并缓存数据/连接的方法。例如扩展提供ext.get_db方法第一次被调用时才创建数据库连接这样不使用数据库的视图就不会创建连接。注册视图如果扩展还想提供一些特定视图可以在init_app中调用register_blueprint把Blueprint注册到应用上。register_blueprint定义于 src/flask/sansio/app.py注册后会记录到应用的blueprints字典中。配置技术多层级配置设计扩展的配置可以来自多个层级和来源开发时应仔细思考哪些部分属于哪一层按应用实例配置app.config值这类配置可能随每次部署而合理变化常见例子是外部资源如数据库的 URL。配置 key 应以扩展名开头避免与其他扩展冲突。按扩展实例配置__init__参数这类配置通常影响扩展如何使用部署之间一般不会变化。按扩展实例配置实例属性与装饰器方法在扩展实例创建之后直接给ext.value赋值或用ext.register装饰器注册函数往往更符合使用习惯ergonomic。全局配置类属性修改Ext.connection_class这类类属性可以在不创建子类的情况下定制默认行为也可与按扩展配置组合来覆盖默认值。子类化并覆写方法与属性让扩展自身的 API 可被覆写为高级定制提供了非常强大的工具。Flask对象本身就同时使用了上述所有技术例如config_class、json_provider_class等类属性可被覆写见 src/flask/sansio/app.py。具体采用哪些配置层级取决于你的扩展需要什么、想支持什么。一个重要警告配置不应在应用设置阶段结束后、服务器开始处理请求之后再修改。配置是全局的对配置的任何修改都无法保证对其他 worker 可见。请求期间的数据g对象的使用规范编写 Flask 应用时g对象用于在请求期间存储信息。例如官方教程 examples/tutorial/flaskr/db.py 把 SQLite 连接存为g.db。扩展同样可以使用g但要格外小心g是一个全局命名空间扩展必须使用不会与用户数据冲突的唯一名字例如以扩展名作为前缀或命名空间# 以扩展名为内部前缀 g._hello_user_id 2 # 或以扩展名为内部命名空间 from types import SimpleNamespace g._hello SimpleNamespace() g._hello.user_id 2g的实现是_AppCtxGlobals见 src/flask/ctx.py一个纯对象提供属性读写、get、pop、setdefault等类字典方法并通过LocalProxy暴露为flask.g。g中的数据持续**一个应用上下文application context**的存活期。应用上下文在一次请求、一条 CLI 命令或一个with app.app_context():块期间是激活的如果要存储的是需要关闭的资源用teardown_appcontext确保应用上下文结束时被关闭如果数据只在请求期间有效、CLI 中不会使用则用teardown_request。从 src/flask/ctx.py 的AppContext.pop()实现可以看到上下文弹出时会先执行teardown_request若在请求中再执行teardown_appcontext这与官方指南的顺序描述一致。教程里的close_db就是注册在app.teardown_appcontext上的典型例子无论请求是否异常结束应用上下文弹出时连接都会被关闭。视图与模型的依赖处理扩展的视图可能需要与数据库中的特定模型、或其他扩展/应用数据交互。例如一个与 Flask-SQLAlchemy 协作、提供Post模型和读写视图的Flask-SimpleBlog扩展Post模型需要继承 Flask-SQLAlchemy 的db.Model但db.Model只有在创建该扩展实例后才可用而不是在扩展定义视图时。那么在模型存在之前定义的视图代码如何访问模型方法一通过视图类传参。利用 src/flask/views.py 中的View/MethodView机制——在__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_keyTrue) title db.Column(db.String, nullableFalse) self.post_model Post def init_app(self, app): api_view PostAPI.as_view(modelself.post_model) db SQLAlchemy() blog BlogExtension(db) db.init_app(app) blog.init_app(app)方法二通过扩展属性 app.extensions。利用self.post_model这样的扩展属性在init_app时把扩展加入app.extensions之后视图里通过current_app.extensions[simple_blog].post_model访问。方法三提供基类。扩展还可以提供基类让用户提供符合扩展期望 API 的自己的Post模型用户实现class Post(blog.BasePost)然后赋给blog.post_model。官方指南坦言这类资源依赖并没有完美方案只有针对不同需求与定制程度的策略取舍。好在绝大多数扩展并不需要这种依赖遇到设计问题时可以到社区Discord、GitHub Discussions讨论。推荐的扩展开发指南Flask 曾有过approved extensions官方认可扩展机制——由 Flask 维护者评估扩展的质量、支持与兼容性后列入名单。虽然该名单后来因难以维护而取消但这些指南对今天所有扩展依然具有参考价值因为它们帮助 Flask 生态保持一致与兼容。扩展需要有维护者如果作者想退出项目应找到新维护者并移交仓库、文档、PyPI 及其他服务的访问权限。GitHub 上的Pallets-Eco组织允许在 Pallets 维护者的监督下进行社区维护。命名规范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应在 requirements 文件中指定测试依赖测试必须包含在 sdist 发行版中。文档PyPI 元数据或 README 中必须有文档或项目网站链接文档应使用 Flask 主题来自官方 Pallets Themes。依赖版本策略依赖不应使用上限或假设特定版本方案而应使用下限来标明最低兼容支持例如sqlalchemy1.4。Python 版本声明使用python_requiresversion声明支持的 Python 版本。Flask 与 Pallets 的政策是支持所有距离 EOL 不到六个月的 Python 版本可参考 Python 官方 EOL 日历。小结查找PyPI 上按Framework :: Flask标签与Flask-Foo/Foo-Flask命名惯例检索扩展。使用绝大多数扩展遵循创建实例 → 配置app.configkey 加扩展名前缀→foo.init_app(app)的两阶段模式。构建扩展类不持有app通过init_app挂载状态存于app.extensionskey 用去前缀后的扩展名行为通过before_request/teardown_request/register_blueprint等 setup 方法添加g对象是请求期数据共享空间但需用唯一前缀配置按应用级、扩展级、类级、子类化分层设计。发布遵循命名、许可证、多应用支持、工厂模式、可编辑安装、测试、文档、依赖下限与python_requires九项社区指南。更多细节可继续阅读 docs/extensiondev.rst扩展开发、docs/appcontext.rst应用上下文与g、docs/views.rst视图与MethodView以及教程中的 examples/tutorial/flaskr/db.py 这一扩展式init_app与teardown_appcontext的实战范例。【免费下载链接】flaskThe Python micro framework for building web applications.项目地址: https://gitcode.com/gh_mirrors/fl/flask创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表