PAYATHON 2026

如何在 Flask 的 HTTP 请求和 CLI 命令中共享数据库会话?

数据阿明

结论

g 中的数据只在当前 Flask 应用上下文中有效。应用工厂中的 with app.app_context(): 会创建一个临时上下文,离开 with 块后,上下文就结束了。后续的 HTTP 请求和 CLI 命令会使用各自的新上下文,因此无法读取之前设置的 g.foo。

@app.before_request 只在处理 HTTP 请求前运行,不会为 CLI 命令初始化 g.foo。

推荐做法:按需创建会话

将会话创建封装在一个函数中:首次调用时,把会话存入当前上下文的 g;同一上下文中再次调用时,则复用已有会话。下面的 create_session() 和 close() 只是示意名称,请替换为所用数据库库提供的创建和清理方法。

from flask import Flask, g

def create_app():
    app = Flask(__name__)

    def get_session():
        if "foo" not in g:
            g.foo = create_session()
        return g.foo

    @app.teardown_appcontext
    def shutdown_session(exception=None):
        session = g.pop("foo", None)
        if session is not None:
            session.close()

    @app.route("/")
    def items():
        session = get_session()
        return "session ready"

    @app.cli.command()
    def seed():
        session = get_session()
        print("session ready")

    return app

HTTP 视图和 CLI 命令都在各自的应用上下文中调用 get_session()。会话会在首次需要时创建并存入该上下文的 g,上下文结束时由 teardown_appcontext 清理。Flask CLI 命令通常会自动使用应用上下文;自行编写命令的调用方式,则需要确认命令执行期间已推入应用上下文。

为什么原代码会报错

原代码在应用工厂执行时设置了 g.foo:

with app.app_context():
    g.foo = 'abc'

g.foo 只在 with 块中的临时上下文里有效。离开代码块后,上下文结束,后续请求和 CLI 命令不会继承其中的 g。before_request 只覆盖 HTTP 请求,因此单独添加这个钩子无法初始化 CLI 命令需要的值。

注意事项

  • 如果所用数据库库提供 remove()、事务回滚或专用的上下文清理方法,应按该库的要求在 teardown_appcontext 中清理。不要假定所有会话都适合直接调用 close()。
  • teardown_appcontext 的回调接收的是异常参数,不是响应对象,因此通常写成 def shutdown_session(exception=None):。
  • 如果不需要懒加载,也可以在每个应用上下文创建时初始化资源。此时初始化钩子必须覆盖应用上下文的生命周期,并确保 HTTP 和 CLI 都会触发。按需 getter 通常更容易避免创建实际不会用到的资源。

备注:内容仅供参考。