← 返回题目列表

Flask 应用工厂模式是什么?为什么推荐 create_app?

高频 中等 第 11 / 27 题 更新于 2026/07/27
Flask应用工厂create_app配置管理

简化版

Flask 应用工厂模式是用 create_app() 函数创建并配置 Flask 应用,而不是在模块导入时直接创建全局 app。它方便按环境加载配置、初始化扩展、注册蓝图、编写测试,也能避免循环导入和全局状态难管理的问题。

详细版

应用工厂通常长这样:

def create_app(config_name=None):
    app = Flask(__name__)
    app.config.from_object(config_map[config_name])

    db.init_app(app)
    migrate.init_app(app, db)

    app.register_blueprint(user_bp)
    app.register_blueprint(order_bp)

    return app

推荐应用工厂的原因:

  • 可以创建多个不同配置的 app,方便测试。
  • 扩展对象可以先创建,再用 init_app 延迟绑定。
  • 配置加载、蓝图注册、错误处理器注册集中管理。
  • 减少循环导入。
  • 更适合生产、测试、开发多环境切换。

面试重点是说明:应用工厂不是语法技巧,而是 Flask 工程化组织方式。

完整版教学

一、为什么直接创建全局 app 会有问题

很多入门代码这样写:

app = Flask(__name__)
app.config["DEBUG"] = True

小项目没问题,但工程一大就会遇到几个麻烦。第一,配置在导入时已经确定,测试时很难创建不同配置的应用。第二,扩展初始化、蓝图注册、模型导入容易互相依赖,形成循环导入。第三,一个进程里如果需要创建多个 app 实例,直接全局 app 会非常不灵活。

应用工厂把“创建应用”变成一个函数调用。每次调用 create_app() 都能得到一个配置完整的 Flask 实例。这让应用创建过程更可控。

二、应用工厂的典型结构

常见项目结构:

app/
  __init__.py
  config.py
  extensions.py
  blueprints/
    user.py
    order.py

extensions.py 里通常不绑定 app:

from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()

__init__.py 中创建应用:

def create_app(config_object):
    app = Flask(__name__)
    app.config.from_object(config_object)

    db.init_app(app)
    migrate.init_app(app, db)

    from .blueprints.user import user_bp
    app.register_blueprint(user_bp)

    return app

这种模式里,扩展对象先独立创建,等 app 出现后再通过 init_app 绑定。这样可以避免扩展在导入阶段就依赖某个具体 app。

三、应用工厂和配置管理

不同环境需要不同配置:开发环境开启 debug,测试环境使用测试数据库,生产环境关闭 debug 并读取安全密钥。应用工厂可以按参数或环境变量加载配置:

def create_app(config_name="development"):
    app = Flask(__name__)
    app.config.from_object(configs[config_name])
    return app

测试时可以这样:

app = create_app("testing")
client = app.test_client()

这样每个测试可以拿到独立配置,不会污染生产配置。面试时提到测试,是应用工厂很重要的加分点。

四、应用工厂如何缓解循环导入

循环导入在 Flask 项目里很常见。比如 views 需要导入 db,app 初始化又导入 views;models 需要 app 配置,app 又需要导入 models。应用工厂通过延迟导入和 init_app 降低这种耦合。

常见做法是:

  • 扩展对象放 extensions.py
  • 蓝图在 create_app() 内部导入并注册。
  • 模型只依赖扩展对象 db,不直接依赖具体 app。
  • 需要当前 app 时使用 current_app,而不是导入全局 app。

这不是说应用工厂能自动解决所有循环导入,但它提供了更清晰的依赖方向。

五、应用工厂和部署入口

使用应用工厂后,生产部署入口可能写成:

from app import create_app

app = create_app("production")

Gunicorn 可以加载这个 app:

gunicorn "wsgi:app"

或者使用支持工厂语法的方式:

gunicorn "app:create_app()"

具体命令取决于项目结构和部署工具。关键是生产环境仍然需要一个 WSGI application 对象,应用工厂负责创建它。

六、常见误区与工程边界

误区一:所有代码都放进 create_app()create_app 应该负责配置应用、初始化扩展、注册蓝图、注册钩子和错误处理器,不应该塞业务逻辑。

误区二:用了应用工厂但扩展仍然在导入时绑定全局 app。这样会破坏工厂模式的意义。扩展对象应优先使用 init_app 延迟绑定。

误区三:在模型或工具函数中导入 app。更推荐使用 current_app 读取配置,或通过函数参数显式传入依赖。

七、常见误区与追问

做法是否推荐原因
模块导入时直接创建全局 app小 demo 可以,工程慎用配置和测试隔离差,容易循环导入
create_app(config)推荐可按环境创建不同 app,集中注册组件
扩展 db.init_app(app)推荐扩展先声明,后绑定具体 app
  • 误区:应用工厂只是把 app 创建代码换个函数名。 它的核心价值是延迟创建、配置隔离、扩展初始化、蓝图注册和测试友好。
  • 误区:所有业务代码都应该塞进 create_app。 create_app 应负责装配应用,不应承载订单、支付、用户注册等业务流程。
  • 误区:用了工厂就不会循环导入。 如果模型、蓝图、扩展仍互相导入具体 app,循环导入仍会出现;关键是延迟绑定和清晰依赖方向。
  • 追问:为什么扩展对象常放 extensions.py? 先定义 db = SQLAlchemy() 等对象,再在工厂里 init_app(app),能避免导入时绑定固定 app。
  • 追问:测试为什么喜欢应用工厂? 测试可以创建使用测试配置、临时数据库或 mock 依赖的 app,不影响生产配置。
  • 追问:生产部署如何调用应用工厂? 可以暴露 wsgi.py 中的 app = create_app(),也可以用 Gunicorn 支持的工厂调用语法。

记忆钩子:应用工厂不是“多一层函数”,而是把 app 的创建、配置、扩展、蓝图注册变成可重复、可测试、可切换的装配过程。

八、加强记忆

Flask 应用工厂就是用 create_app() 统一创建和配置应用。它让配置、扩展初始化、蓝图注册和测试环境切换更清晰,也能减少全局 app 带来的循环导入和状态污染问题。大型 Flask 项目通常会把应用工厂、蓝图和扩展延迟初始化配合使用。