Flask 笔记十二:用环境变量管理配置

前面几篇里,SECRET_KEY、数据库地址多半写在 app/__init__.py 里,本地开发很方便。但一旦 部署到服务器、多人协作、把代码推到 Git,硬编码会带来三类问题:

  1. 密码进仓库 --- 泄露风险
  2. 开发 / 生产配置混在一起 --- 改一处误伤另一处
  3. 每台机器配置不同 --- 数据库地址、调试开关没法灵活换

这一篇做一件事:把敏感和易变的配置挪到环境变量,代码只负责「读配置」,不负责「记密码」。

例子仍是通用的 Note 备忘录项目,不涉及任何真实业务。


1. 学完后你能做什么

  • os.environ 读取配置
  • .env 文件,本地自动加载(不提交到 Git)
  • SECRET_KEYDEBUG、数据库 URI 按环境切换
  • 知道上线时 FLASK_DEBUG=0 要注意什么
  • 区分 哪些该放环境变量、哪些可以写死在代码里

2. 环境变量是什么

操作系统里的一种「键值对」。程序启动时读取,不改代码就能换配置。

终端里临时设置(只对当前 shell 有效)

export SECRET_KEY="一串随机长字符串"

export FLASK_DEBUG=1

python manage.py

Flask 里读取:

import os

secret = os.environ.get("SECRET_KEY")

debug = os.environ.get("FLASK_DEBUG")

os.environ.get("KEY") --- 没有时返回 None,不会报错。

os.environ["KEY"] --- 没有时直接抛异常,一般 get 更安全。


3. 哪些配置应该外置

配置 为什么外置 示例环境变量
Session 签名密钥 泄露可伪造登录 SECRET_KEY
数据库连接 每台机器不同 DATABASE_URL
第三方 API Key 绝对不进仓库 SOME_API_KEY
是否调试模式 生产必须关 FLASK_DEBUG
邮件密码 敏感 SMTP_PASSWORD

可以写死在代码里的:上传目录相对路径、分页默认每页 10 条、业务常量------不含秘密、各环境相同 的值。


4. 改造 app/__init__.py(核心写法)

在第二篇基础上,把硬编码改成读环境变量:

import os

from flask import Flask

def _env_bool(key: str, default: bool) -> bool:

"""把环境变量转成布尔:1 / true / yes / on 视为 True。"""

v = os.environ.get(key)

if v is None or v.strip() == "":

return default

return v.strip().lower() in ("1", "true", "yes", "on")

app = Flask(name)

开发兜底;生产务必在环境里设 SECRET_KEY

_dev_fallback = "dev-only-change-me-in-production"

app.config"SECRET_KEY" = (

(os.environ.get("SECRET_KEY") or "").strip() or _dev_fallback

)

未设置时默认开发调试;生产设 FLASK_DEBUG=0

app.config"DEBUG" = _env_bool("FLASK_DEBUG", True)

app.debug = app.config"DEBUG"

数据库:优先完整 URI,否则拼 SQLite 默认路径

basedir = os.path.abspath(os.path.dirname(file))

db_uri = (os.environ.get("DATABASE_URL") or "").strip()

if not db_uri:

db_path = os.path.join(basedir, "..", "instance", "app.db")

db_uri = "sqlite:///" + db_path

app.config"SQLALCHEMY_DATABASE_URI" = db_uri

app.config"SQLALCHEMY_TRACK_MODIFICATIONS" = False

习惯可以记成:

os.environ.get("KEY") → 有就用

没有 → 给安全的本地默认值(仅开发)

生产 → 必须在环境里显式设置


5. 用 .env 文件,本地不用每次 export

每次开终端都 export 很麻烦。常见做法:项目根目录放 .env,本地开发自动加载。

方式 A:python-dotenv(推荐)

pip install python-dotenv

manage.py 最顶部(在 from app import app 之前):

from dotenv import load_dotenv

load_dotenv() # 读取项目根目录 .env,写入 os.environ

from app import app

方式 B:自己写几行(不装包也行)

def load_local_env():

path = os.path.join(os.path.dirname(file), ".env")

if not os.path.isfile(path):

return

with open(path, encoding="utf-8") as f:

for line in f:

line = line.strip()

if not line or line.startswith("#"):

continue

key, _, val = line.partition("=")

key, val = key.strip(), val.strip()

if key and key not in os.environ: # 不覆盖已有环境变量

os.environkey = val

原则:shell 里已经 export 的值优先;.env 只补空缺。


6. .env 示例(本地开发)

项目根目录新建 .env(不要提交到 Git):

本地开发配置

SECRET_KEY=local-dev-secret-not-for-production

FLASK_DEBUG=1

SQLite 可不写,走代码里的默认路径

DATABASE_URL=sqlite:///instance/app.db

若改用 MySQL:

DATABASE_URL=mysql+pymysql://root:你的密码@127.0.0.1:3306/notes_db

.gitignore 里加上:

.env

*.env.local

可以提交 .env.example(无真实密码),告诉同事要配哪些项:

SECRET_KEY=请换成随机长字符串

FLASK_DEBUG=1

DATABASE_URL=


7. 生成安全的 SECRET_KEY

不要用 123456dev-secret-key 上线。

python -c "import secrets; print(secrets.token_hex(32))"

输出类似 a3f8c2...,复制到生产环境变量或 .envSECRET_KEY

换了 SECRET_KEY 以后,已有用户的 Session Cookie 会失效,需要 重新登录------部署前要有心理预期。


8. 开发 vs 生产:怎么区分

不必搞很复杂,入门阶段 靠环境变量切换 就够:

场景 FLASK_DEBUG SECRET_KEY 数据库
本地 .env 1 本地随意(仍别用太简单) SQLite
生产服务器 0 随机 64 字符 MySQL 等

生产服务器上(示例):

export SECRET_KEY="生产环境随机密钥"

export FLASK_DEBUG=0

export DATABASE_URL="mysql+pymysql://user:pass@db-host:3306/notes"

export SESSION_COOKIE_SECURE=1 # 仅 HTTPS 站点

用 systemd、Docker、云平台「环境变量面板」设置,效果一样------不写进代码。


9. FLASK_DEBUG=0 上线时要注意什么

① 关掉调试页

DEBUG=True 时,报错会显示 Werkzeug 调试栈,可能暴露代码路径和变量。

生产 必须 FLASK_DEBUG=0

② 错误页用第八篇的自定义 404 / 500

用户看不到栈信息,只看到友好页面。

若站点走 HTTPS,建议:

app.config"SESSION_COOKIE_SECURE" = _env_bool("SESSION_COOKIE_SECURE", False)

app.config"SESSION_COOKIE_HTTPONLY" = True

本地 http://127.0.0.1 不要开 SESSION_COOKIE_SECURE------浏览器不会给非 HTTPS 发带 Secure 的 Cookie,登录会失效。

④ 生产未设 SECRET_KEY 要报警

可在 app/__init__.py 加一段检查:

import warnings

if not app.config"DEBUG" and app.config"SECRET_KEY" == _dev_fallback:

warnings.warn(

"生产环境必须设置环境变量 SECRET_KEY,不要使用代码默认值。",

UserWarning,

)

启动时看到警告,说明配置漏了。


10. 批处理脚本与 Web 共用配置

第十篇、十一篇的 note_service 可能被 脚本 调用(定时清理、导出 CSV)。脚本没有 Flask 请求,但 数据库地址应和 Web 一致。

做法:脚本开头同样 load_dotenv(),再 from app import app, db

from dotenv import load_dotenv

load_dotenv()

from app import app, db

from app.note_service import list_all_notes

with app.app_context():

page = list_all_notes(per_page=1000)

...

同一套 DATABASE_URL,Web 和脚本不会连到不同库。


11. 流程示意

启动 manage.py

load_dotenv() 读 .env(本地)

app/init.py 读 os.environ

├─ SECRET_KEY → app.config

├─ FLASK_DEBUG → app.config"DEBUG"

└─ DATABASE_URL → SQLALCHEMY_DATABASE_URI

Flask 启动,Session / 数据库按当前环境工作

git push(不含 .env)

服务器用面板 / export 设生产环境变量

同一套代码,不同配置,连生产数据库


12. 新手常踩的 6 个坑

坑 1:.env 提交到 Git

密码进仓库,即使用户删了,历史记录里可能还在。

.gitignore + 只提交 .env.example

坑 2:生产忘了设 SECRET_KEY

大家共用代码里的 dev 默认值,Session 可被预测伪造。

生产必须单独生成随机密钥。

坑 3:生产 FLASK_DEBUG 仍为 1

报错页暴露内部信息,且性能和安全都有风险。

上线 checklist 第一项:DEBUG=0。

HTTPS 本地很少,Cookie 发不出去,表现为「登录立刻掉」。

本地 False,生产 HTTPS 再 True。

坑 5:环境变量名前后不一致

代码读 DATABASE_URL.envDB_URL------读不到,默默走默认 SQLite。

团队定一张配置表,名字统一。

坑 6:在代码里写真实 API Key「临时一下」

「临时」往往会留很久。

秘密只进环境变量,不进 .py 文件。


13. 实际项目里怎么用

  • 敏感项 全部环境变量;代码里只有读取逻辑和开发兜底
  • 仓库放 .env.example,新人复制成 .env 再改
  • 生产用平台环境变量,不把 .env 文件拷到服务器(除非有安全的密钥管理)
  • Web、脚本、定时任务 共用同一套数据库相关变量
  • 部署前检查:DEBUG=0SECRET_KEY 已设、HTTPS Cookie 已对齐

14. 小结

记住四件事:

  1. 秘密不进代码 --- SECRET_KEY、数据库密码、API Key 走环境变量
  2. .env 本地用、不进 Git --- 配合 load_dotenv()
  3. FLASK_DEBUG --- 开发 1,生产 0
  4. 同一变量多处复用 --- Web 和脚本读同一个 DATABASE_URL