🧪 第1章 建立最基础的应用程序
📄 一句话概括

配置的硬度顺序:环境变量 > .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 秒实验

  1. 把 .env 里某一行加上引号 SECRET_KEY="abc",看 dotenv_values 读出来带不带引号。
  2. 用 from_prefixed_env("APP"),把环境变量改成 APP_SECRET_KEY,看还认不认。
  3. 在 .env 里写一行 FLASK_DEBUG=1,然后跑 flask routes 看它认不认这个文件。

常见误区

  1. 把 .env 提交进仓库:密钥泄露最常见的一条路径,删了也没用,git 历史还在。
  2. 以为 load_dotenv() 会覆盖已有变量:默认不覆盖,这是好设计,别去关它。
  3. 用 FLASK_ENV 控制环境:3.1 里彻底失效,而且静默失败。
  4. 在代码里写 if 环境 == "生产":环境判断散在各处,最后没人说得清线上到底是哪个分支。
自测三问(点开对答案) 1. 配置的三层来源,谁压谁? —— 环境变量压过 .env,.env 压过代码里的默认值。 2. `from_prefixed_env()` 默认读什么前缀? —— `FLASK_`。读进来时前缀会被剥掉,`FLASK_SECRET_KEY` 变成 `SECRET_KEY`。 3. 为什么 `FLASK_ENV` 失效比报错更麻烦? —— 它静默忽略,你以为开了除错模式,实际没开,还找不到原因。

配置解决了「值从哪来」,下一篇解决「地址怎么对」:路由规则、五种转换器,还有 url_for。

相关笔记