Flask 3.0 强制要求 Werkzeug 3.0+ 且 Python ≥3.8,废弃 FLASK_ENV、不兼容旧中间件与部分扩展,async 视图需显式 await,配置加载更严格,WSGI 服务器需适配 ASGI 或协程库。

Flask 3.0 强制要求 Werkzeug 3.0+,旧版中间件不再兼容
Flask 3.0 不再支持 Werkzeug 2.x,而 Werkzeug 2.3.x 是最后一个兼容 Python 3.7 的版本。如果你还在用 Werkzeug==2.2.3 或更老的版本,升级 Flask 到 3.0 会直接报错:ImportError: cannot import name 'url_encode' from 'werkzeug.urls'(该函数在 Werkzeug 3.0 中被移入 werkzeug.urls 子模块并重命名)。这不是警告,是硬性依赖断裂。
- 必须同步升级
Werkzeug>=3.0.0,且注意其 Python 版本下限已升至3.8+ -
flask run命令底层逻辑重构,不再接受--reload-type=stat这类旧参数 - 部分第三方扩展(如
Flask-Login==0.6.3)需至少更新到0.6.4才能通过current_user的类型检查
异步路由不再是实验功能,但 async def 必须配 await
Flask 3.0 把 @app.route 对协程的支持从 opt-in 变成默认行为,但这也带来一个容易忽略的执行陷阱:函数声明为 async def 后,内部所有 I/O 操作(比如数据库查询、Redis 调用)都必须显式 await,否则会返回 coroutine object 而不是实际值,最终导致响应体变成 <coroutine object at></coroutine> 字符串。
- 常见错误:写了
async def api(): return db.query(...)—— 这里db.query若返回的是 coroutine,不加await就直接返回了 - WSGI 服务器(如 Gunicorn 默认 worker class)仍不支持原生 async,必须搭配
gevent或eventlet,或改用 ASGI 服务器(如 Uvicorn) -
request.get_json()在 async 视图中仍是同步阻塞调用,不能await,这点和 FastAPI 不同
配置加载时机变化,app.config.from_object() 现在更严格
Flask 3.0 对配置对象的属性读取做了延迟验证。过去你可能把未定义的配置项写进类里(比如 class Config: SECRET_KEY = os.getenv('KEY') or 'dev'),Flask 2.x 会静默容忍;但在 3.0 中,只要该类被传入 app.config.from_object(Config),就会立刻尝试解析所有属性 —— 如果 os.getenv('MISSING_VAR') 返回 None 且后续代码用了 or 拼接字符串,就可能触发 TypeError: can only concatenate str (not "NoneType") to str。
- 建议把配置计算逻辑移到
__init__.py或工厂函数内,而非配置类属性中 -
app.config.from_mapping()替代了部分update()场景,它不会触发属性访问,适合动态注入 -
FLASK_ENV环境变量彻底废弃,FLASK_DEBUG也不再影响app.debug,一切以app.config['DEBUG']为准
扩展兼容性断层比表面看起来更深
很多扩展宣称“支持 Flask 3.0”,但实际只测了最简路由场景。真实生产环境里容易暴露问题:
立即学习“Python免费学习笔记(深入)”;
-
Flask-SQLAlchemy 3.0+要求SQLAlchemy>=2.0,而 SQLAlchemy 2.0 的session.execute()返回Result对象,不是元组列表,旧代码for row in session.execute(...): print(row[0])会报TypeError: 'Row' object is not subscriptable -
Flask-Migrate升级后,flask db upgrade可能因 Alembic 配置变更失败,错误信息常为AttributeError: 'Config' object has no attribute 'script_location' - 自定义 CLI 命令若依赖
app.cli.command()的旧装饰器签名,需重写为接受app参数的 factory 形式
真正卡住升级的往往不是 Flask 本身,而是你依赖树里那个没更新 README 的小众扩展 —— 它可能还在用 flask._compat 或硬编码 werkzeug.wrappers.BaseResponse。


















