工厂模式核心是目录隔离与导入时机控制:create_app须置于app/__init__.py作为包入口,扩展实例定义于extensions.py并用init_app延迟绑定,蓝图注册须在配置与扩展初始化之后,测试和CLI需避免顶层调用create_app。

工厂模式不是加个 create_app 函数就完事了——目录结构没理顺,create_app 会立刻变成循环导入的导火索。
为什么 create_app 放在 app/__init__.py 里最稳妥
因为这是 Python 包加载的「第一入口」,且不会被其他模块提前 import。如果把 create_app 放在 app/factory.py 或 app/core.py,路由、模型、CLI 命令等模块很容易在初始化时反向 import 它,触发 ImportError: cannot import name 'db' from 'app.extensions'。
-
app/__init__.py只做三件事:定义create_app、声明包级变量(如__all__ = ['create_app'])、留空或写极简说明 - 绝对不要在
app/__init__.py里 importmodels、routes或任何业务模块 - 所有扩展实例(
db、login_manager)必须定义在app/extensions.py,且不传app给构造函数
extensions.py 必须用 init_app() 而非直接绑定
直接写 db = SQLAlchemy(app) 会让扩展强依赖 app 实例,导致测试时无法复用 db 对象,也破坏了工厂的延迟初始化逻辑。
- 正确写法:
db = SQLAlchemy()在extensions.py;db.init_app(app)在create_app内部调用 - 同理,
mail = Mail()→mail.init_app(app),cache = Cache()→cache.init_app(app) - 若某扩展不支持
init_app(极少见),要么换扩展,要么封装一层代理类,避免在模块顶层触发初始化
蓝图注册必须在配置加载之后、扩展初始化之后
顺序错一拍,url_for() 就会报 RuntimeError: Attempted to generate a URL without the application context,或者蓝本里的 @bp.before_request 拿不到配置项。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
立即学习“Python免费学习笔记(深入)”;
- 标准流程只能是:
app = Flask(...)→app.config.from_object(...)→ext.init_app(app)→app.register_blueprint(...) - 蓝图内部禁止 import
current_app或g,所有上下文感知操作应放在视图函数内或使用@bp.before_app_request - 推荐把蓝图注册抽成独立函数(如
_register_blueprints(app)),和配置、扩展初始化逻辑物理隔离,方便单元测试替换
测试和 CLI 命令最容易踩的坑:别在模块顶层调用 create_app
tests/conftest.py 或 app/cli.py 里直接写 app = create_app('testing') 看似省事,实则埋雷——一旦某个测试文件或命令模块被 pytest 或 Flask CLI 预加载,它就会污染后续所有测试的 app 上下文。
- 测试中应始终用 fixture 封装:
@pytest.fixture函数内调用create_app,并用app.app_context()显式激活 - CLI 命令必须注册到
app.cli,而不是自己 new 一个 app;命令函数体内再通过current_app获取实例 - 本地启动脚本
run.py可以调用create_app,但仅限于if __name__ == '__main__':块内,且不能被其他模块 import
真正难的不是写出 create_app,而是让每个模块都「忍住不提前伸手要 app」——所有延迟,都要靠目录隔离和导入时机控制来守住。

















