bigywater.tech
Flask · 71

Extension 初始化时机

扩展什么时候读配置?为什么 init_app 之后改配置有时生效、有时不生效?一个扩展对象能不能给多个应用用?这篇用 SQLAlchemy、Flask-Caching、Flask-Login 和两个自己写的小扩展逐项验证。

init_app
扩展与应用绑定的入口,状态存在 app.extensions
读一次
SQLAlchemy 与 Flask-Caching 在 init_app 时读配置,之后改不生效
current_app
自己写扩展时,在方法里读配置才能跟着应用走

构造与 init_app

实测 · 本机执行输出
  SQLAlchemy() 先不带 app 创建:此时还没有绑定任何应用
  init_app 之前访问 db.engine → RuntimeError: Working outside of application context.
  init_app 之后,应用上下文里 db.engine = Engine
  用别的(没有 init_app 的)应用的上下文访问 → RuntimeError: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you c
  app.extensions 里登记了: ['sqlalchemy']

上一篇:Application Factory;下一篇:Flask-Login 登录流程。

配置读一次

实测 · 本机执行输出
  init_app 之后再把 URI 改成 other.db,引擎实际指向: sqlite:///%3Amemory%3A
  init_app 时没有配置 URI → RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set.

不同扩展的差别

实测 · 本机执行输出
  Flask-Caching:init_app 后把 CACHE_TYPE 改成 NullCache,cache.get 仍是 1 (用的是 init_app 时的 SimpleCache)
  Flask-Login:init_app 之后设置 login_view 仍然有效: late_login

一个扩展多个应用

实测 · 本机执行输出
  同一个 db 对象,在 x、y 两个应用上下文里得到的引擎不同: True

工厂里的顺序

实测 · 本机执行输出
  先 init_app 再设配置 → RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set.
  先设配置再 init_app → 成功

自己写扩展

实测 · 本机执行输出
  init_app 之后改配置:LazyExt.greet() = 改后(lazy) | EagerExt.greet() = hello
  EagerExt 对象被两个应用共用时(把值存在 self 上):g_ 的上下文里 greet() = g2 的值 (被第二次 init_app 覆盖了)

这一节是后面第 80 篇「自定义扩展」的铺垫。

分步演示

Extension 初始化时机
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。Flask-Caching 2.5.1、Flask-Login 0.6.3
    • 没有验证:Flask-SQLAlchemy 的 SQLALCHEMY_ENGINE_OPTIONS 等其它配置项的读取时机、其它扩展的 init_app 细节、扩展用 app.extensions 以外方式存状态(如模块全局变量)时的多线程行为
    京ICP备2026067502号-1京公网安备11010502063487号