应只锁主框架如flask==2.3.3,间接依赖用>=或不写版本;用pip-tools从requirements.in生成带哈希的requirements.txt;避免锁Werkzeug等共生依赖,按兼容矩阵匹配版本。

requirements.txt 里写死版本号反而容易出问题
很多人以为把所有包都用 == 锁死版本就能避免冲突,结果部署时发现 flask 和 werkzeug 不兼容,或者 click 升级后 flask run 直接报错。根本原因是 Flask 的依赖有隐式约束——它不只看自己的 setup.py,还依赖运行时实际加载的子依赖版本。
实操建议:
- 主应用只锁关键包(如
flask==2.3.3),其他间接依赖用>=或不写版本(让 pip 自动解出兼容组合) - 用
pip install --no-deps flask==2.3.3先装主框架,再用pip install -e .或pip install -r requirements.txt让 pip 统一解决依赖树 - 避免手动在
requirements.txt里逐行加==,尤其别锁itsdangerous、jinja2这类 Flask 深度耦合的包
用 pip-tools 生成可复现的 requirements.in
直接手写 requirements.txt 很难兼顾开发便利和环境一致性。常见错误是:本地能跑,CI 构建失败;或者 pip freeze > requirements.txt 导出一堆 dev-only 包(比如 black、pytest),导致线上环境装了不该装的东西。
实操建议:
- 新建
requirements.in,只写你明确需要的包,比如:flask>=2.2.0
sqlalchemy>=1.4 - 运行
pip-compile requirements.in生成带完整依赖树和哈希的requirements.txt - 用
--extra-index-url或--find-links控制私有源,避免 pip-tools 默认只走 PyPI - CI 中用
pip install --require-hashes -r requirements.txt强制校验哈希,防篡改
Flask 启动时报 AttributeError: 'NoneType' object has no attribute 'app' 怎么定位
这错误不是 Flask 本身的问题,而是依赖版本错配触发的初始化顺序异常。典型场景是 flask-sqlalchemy 装了 3.x,但 Flask 是 2.2.x,而 flask-sqlalchemy 3.x 内部调用了 Flask 2.3+ 才有的 app.app_context() 方法。
实操建议:
- 先查
pip list | grep -i flask,确认flask、werkzeug、flask-sqlalchemy三者版本是否在官方兼容矩阵内(比如 Flask 2.2.x 对应 Werkzeug >=2.2,<2.4) - 临时删掉
requirements.txt中所有==,只留flask和核心扩展名,重装观察是否恢复 - 用
pip show flask werkzeug看各自 Requires-Dist 字段,交叉比对有没有矛盾约束 - 如果必须用新扩展(如
flask-login 0.6.3),就同步升 Flask 到 2.3+,别单独升一个
Docker 部署时 requirements.txt 安装慢或失败
不是网络问题,而是 pip 在多层依赖下反复回溯求解,尤其当 requirements.txt 里混着 ==、>=、无版本三种写法时,pip 会尝试上百种组合才放弃。
实操建议:
- Dockerfile 中用
RUN pip install --no-cache-dir -r requirements.txt,禁用缓存反而更快(避免 pip 复用旧 wheel 导致冲突) - 把
build-system.requires里的setuptools、wheel提前安装,避免构建时动态拉取 - 生产镜像中删掉
pip list里所有-dev、test、lint相关包,它们可能拖慢安装并引入冲突 - 如果用 Poetry 或 Pipenv,别在 Docker 里再转成
requirements.txt,直接pip install .更可靠