配置的硬度顺序:环境变量 > .env 文件 > 代码默认值,一层压一层。
from_prefixed_env() 默认只认 FLASK_ 开头的键,环境里叫 FLASK_SECRET_KEY,
配置里就叫 SECRET_KEY。
FLASK_ENV 在 3.1 里彻底失效,连配置项 ENV 都不存在了。
这一篇对应教材 1.2.4 节「使用 .env 设定环境变数」,是全书唯一一次专门讲配置。 这一篇也是「教材写法过时」最集中的一处,三条坑有两条在这里。
一、为什么不能写死在代码里
密钥、数据库地址、邮件账号,这三样一旦写进 .py 文件,就有两个麻烦。
第一,进仓库就泄露了,而且删掉也没用——git 历史里还在。
第二,开发、测试、生产三套环境要改同一个文件,改来改去必然出错。
所以规矩是:代码里只写「从哪个键读」,不写「值是什么」。值放环境变量里,谁部署谁填。 这就是所谓 12-Factor 里的第三条:配置存在环境里。
二、.env 文件长什么样
本地开发总不能每次都手动 export 一大堆变量,所以有了 .env:一行一个键值对。
SECRET_KEY=dev-secret-please-change
DATABASE_URL=sqlite:///app.db
[email protected]
不加引号、不加 export、不写空格。它是一份文件,不是一段 shell 脚本。
.env 必须进 .gitignore。
真正进仓库的是 .env.example,里面只有键名和占位值,谁拉下来复制一份改名就行。
三、两个工具函数,区别很大
| 函数 | 做了什么 | 什么时候用 |
|---|---|---|
| dotenv_values | 只解析成字典,不改环境 | 看这份文件有什么 |
| load_dotenv | 写进 os.environ | 让整段程序都能读到 |
load_dotenv() 有个默认行为要记住:
它不会覆盖已经存在的环境变量。所以容器里设的值优先于 .env 文件,这个顺序正合适。
想强制覆盖得传 override=True,但生产环境别这么干——那等于让一个文件压过部署配置。
四、Flask 自己的读法:from_prefixed_env
Flask 2.1 起自带一个方法,把环境变量按前缀批量读进配置:
app.config.from_prefixed_env()
默认前缀是 FLASK。所以环境里叫 FLASK_SECRET_KEY,配置里就成了 SECRET_KEY——前缀被剥掉了。
键名里没有前缀的不认。这次实测就栽在这儿:.env 里写 SECRET_KEY,
from_prefixed_env() 读出来是 None。
如果要换成自己的前缀,传参就行:from_prefixed_env("APP"),
那样 APP_DATABASE_URL 会变成配置里的 DATABASE_URL。
五、教材那条已经不能用了
教材用 FLASK_ENV=development 来开除错模式。这在 2.3 就废弃,3.0 起移除。
这次实测的结果比「报错」更麻烦:它不报错、不警告,直接忽略。设了 FLASK_ENV,问配置要 ENV,
回答是「没有这个键」。
于是你改了代码,浏览器不自动刷新,你会怀疑是自己没保存、是缓存、
是文件路径错了——就是想不到是除错模式压根没开。
正确写法只有一条:FLASK_DEBUG=1。它在 3.1 里照样有效,实测 app.debug 会变成 True。
六、动手 Demo:.env 与环境变量
这一节的 Demo 把 .env 从解析到生效整条链路走一遍,再演示「谁压谁」,
最后验一下教材那条为什么失效。
.env 是现写的临时文件,load_dotenv 与 from_prefixed_env 都是真跑。
import os
from dotenv import dotenv_values, load_dotenv
from flask import Flask
with open(".env", "w") as f: # 现场写一份,省得手建
f.write("SECRET_KEY=dev-secret\n")
print(dotenv_values(".env")) # 只解析,不改环境
load_dotenv(".env") # 写进 os.environ
os.environ["FLASK_SECRET_KEY"] = "from-env"
app = Flask(__name__)
app.config.from_prefixed_env()
print(app.config["SECRET_KEY"]) # from-env
实跑输出:
--------------------------------------------------------------------
.env 与环境变量
--------------------------------------------------------------------
① .env 长什么样
--------------------------------------------------------------------
SECRET_KEY=dev-secret-please-change
DATABASE_URL=sqlite:///app.db
[email protected]
! 一行一个键值对,不加引号、不加 export。它是给人看的,不是给 shell 吃的。
② 读进来
--------------------------------------------------------------------
dotenv_values 只解析、不污染环境,读到 3 个键:
SECRET_KEY = dev-secret-please-change
DATABASE_URL = sqlite:///app.db
MAIL_FROM = [email protected]
load_dotenv 之后 os.environ 里有了:SECRET_KEY=dev-secret-please-change
DATABASE_URL=sqlite:///app.db
app.config.from_prefixed_env() 读到 SECRET_KEY = from-flask-prefix
! 默认前缀是 FLASK:环境里叫 FLASK_SECRET_KEY,配置里就叫 SECRET_KEY。
前面那个 SECRET_KEY 没有前缀,它不认——这就是上面那行原来是 None 的原因。
③ 三种来源,谁压谁
--------------------------------------------------------------------
代码里先写死 from-code
再让 FLASK_SECRET_KEY=from-env 覆盖,结果是:from-env
| 来源 | 硬度 |
|---|---|
| 代码里的默认值 | 最软,随时被压 |
| .env 或 .flaskenv 文件 | 本地开发用,不进仓库 |
| 真正的环境变量 | 最硬,容器里就是靠它 |
! 顺序记牢:环境变量 > .env > 代码默认值。
容器里没有 .env 文件,全靠环境变量,所以代码里必须有兜底默认值。
④ 教材那条已经不能用了
--------------------------------------------------------------------
设了 FLASK_ENV=development,再问 app.config 要 ENV:(没有这个键)
3.1 的配置里根本没有 ENV 这个键,FLASK_ENV 也不再生效。
! 改法:除错用 FLASK_DEBUG=1;要区分环境,自己在配置里加一项 ENV 或 FLASK_CONFIG,别指望框架给。
.flaskenv 这个文件名教材也提过,它是给 flask 命令行用的,3.1 仍然认,
但里面不该再出现 FLASK_ENV。
dotenv_values 读到 3 个键,一个不多一个不少,环境里什么都没变。
from_prefixed_env() 必须配 FLASK_ 前缀。没前缀的键它读成 None,这一条比想象中容易踩。
覆盖顺序是真跑的:代码里写 from-code,环境里设 from-env,最后读到的是 from-env。
最后一节问到 ENV 这个配置项,回答是「没有这个键」——这就是 FLASK_ENV 失效的实证。
30 秒实验
- 把
.env里某一行加上引号SECRET_KEY="abc",看dotenv_values读出来带不带引号。 - 用
from_prefixed_env("APP"),把环境变量改成APP_SECRET_KEY,看还认不认。 - 在
.env里写一行FLASK_DEBUG=1,然后跑flask routes看它认不认这个文件。
常见误区
- 把
.env提交进仓库:密钥泄露最常见的一条路径,删了也没用,git 历史还在。 - 以为
load_dotenv()会覆盖已有变量:默认不覆盖,这是好设计,别去关它。 - 用
FLASK_ENV控制环境:3.1 里彻底失效,而且静默失败。 - 在代码里写
if 环境 == "生产":环境判断散在各处,最后没人说得清线上到底是哪个分支。
自测三问(点开对答案)
1. 配置的三层来源,谁压谁? —— 环境变量压过 .env,.env 压过代码里的默认值。 2. `from_prefixed_env()` 默认读什么前缀? —— `FLASK_`。读进来时前缀会被剥掉,`FLASK_SECRET_KEY` 变成 `SECRET_KEY`。 3. 为什么 `FLASK_ENV` 失效比报错更麻烦? —— 它静默忽略,你以为开了除错模式,实际没开,还找不到原因。配置解决了「值从哪来」,下一篇解决「地址怎么对」:路由规则、五种转换器,还有 url_for。
- ☐ (本篇完,下一篇 1.4 路由:URL 规则、变量与转换器)