Flask全局app对象数据共享问题:视图修改全局属性不生效
你操作的核心错误
你在视图里用from flask import current_app as app拿到的app,不是你在__init__.py里创建的那个原始Flask实例,它是Flask提供的应用上下文代理对象。
这个代理的作用是帮你在不同的上下文环境(比如请求、CLI命令)中获取正确的app实例,但它的属性赋值逻辑有坑:当你直接给app.gcp赋值时,这个值会存在代理对象本身,而不会同步到原始的Flask app实例上。所以其他请求通过current_app访问gcp时,还是会拿到原始app实例上的初始值A,自然看不到你修改的B。
另外,直接给Flask app实例添加自定义属性(比如app.gcp)本身也不是Flask推荐的做法,容易遇到上下文隔离的各种问题。
正确的解决方案
根据你的需求(修改后全局生效),分两种场景给出方案:
场景1:单进程/单线程部署(比如开发环境用app.run())
这种情况下,直接导入原始的app实例来修改就可以:
# aview.py from yourapp import app # 这里的yourapp是你的包名,对应__init__.py所在的目录 from flask import render_template @app.route('/abc') def foo(): print(app.gcp) # 此时访问的是原始app实例的gcp属性 app.gcp = googleCloudSessionObjectB() # 直接修改原始app的属性,全局生效 return render_template('base.html')
场景2:多进程/多worker部署(比如生产环境用Gunicorn多worker)
这种情况下,每个worker进程都有自己独立的app实例和内存空间,直接修改内存中的全局变量只能在当前worker生效,其他worker看不到。这时候需要用外部共享存储来保存你的googleCloudSessionObject状态,比如:
- 把对象序列化后存在Redis中
- 存在数据库里
- 使用分布式缓存服务
如果还是想在代码层面用全局变量的方式管理,可以单独创建一个全局状态模块,逻辑更清晰:
# 新建global_state.py gcp_session = None
# __init__.py from flask import Flask from .global_state import gcp_session app = Flask(__name__) gcp_session = googleCloudSessionObjectA()
# aview.py from flask import render_template from yourapp import app from yourapp.global_state import gcp_session @app.route('/abc') def foo(): print(gcp_session) global gcp_session # 函数内重新赋值需要声明global gcp_session = googleCloudSessionObjectB() return render_template('base.html')
注意:这个方式在多进程部署下同样只能在当前worker生效,跨worker同步必须依赖外部存储。
额外提醒
如果你的googleCloudSessionObject是请求级别的状态(每个请求独立,不需要跨请求共享),那应该用Flask提供的flask.g对象,它是专门用来存储请求上下文内临时数据的,用法如下:
from flask import g @app.before_request def before_request(): g.gcp = googleCloudSessionObjectA() @app.route('/abc') def foo(): print(g.gcp) g.gcp = googleCloudSessionObjectB() # 只在当前请求生效 return render_template('base.html')
不过看你的需求是要全局生效,这个可能不是你需要的,但还是提一下供参考。
内容的提问来源于stack exchange,提问作者caliph

