本文能帮你解决什么✔️ 敏感信息泄漏风险再也不会把密钥传到 GitHub✔️ 快速切换开发/测试/生产环境改一个变量就行✔️ 用 pydantic_settings 自动校验配置少写一堆 try-except 主要内容脉络咱们先从一个痛点说起然后一步步把硬编码改成环境变量再升级到pydantic_settings 的 BaseSettings顺便说清 dotenv 和 pydantic_settings 到底怎么选。最后还会聊多环境配置覆盖和优先级那点事儿。 第一部分硬编码的痛你可能正在经历很多项目一开始为了“快”直接把数据库连接信息写成字符串。像这样DATABASE_URL postgresql://user:pass123localhost/db看起来没毛病直到你需要部署到服务器、要换一套数据库地址。要么你手动改代码打包要么写一堆 if-else 判断环境。更可怕的是万一代码仓库公开密码就等于贴在了公告栏上。你可能会问“那我加到 .gitignore 里不就行了” 是的但配置本身还是散落在各处团队一多照样乱套。 第二部分从环境变量到 Settings 类步步为营好咱们先来第一步——把秘密藏进环境变量。在终端 export 或者在系统里设置一个DATABASE_URL然后代码里用os.getenv去取。这样至少不会把密码写在代码里了。但是你很快会发现每次重启机器环境变量就丢了而且得挨个给队友配一遍极其麻烦。这时候就该.env 文件出场了。接下来重点来了——怎么把 .env 用起来到底是选python-dotenv还是pydantic_settings这个工具的选择好比选螺丝刀不是最贵的就好而是看你的场景。如果你只是想要一个简单的 key-value 读取dotenv 足够轻量但一旦你需要类型校验、字段默认值、多环境自动切换pydantic_settings 的 BaseSettings 绝对是更聪明的选择。再说个容易翻车的点.env 文件一定要立刻加到 .gitignore 里并且提供一个.env.example模板文件给队友。我有次就是忘了这一步差点把密钥推上去。‍ 第三部分实战 —— 用 BaseSettings 管好你的配置直接上代码一看就懂from pydantic_settings import BaseSettings class Settings(BaseSettings): app_name: str MyFastAPI database_url: str secret_key: str debug: bool False class Config: env_file .env env_file_encoding utf-8 settings Settings()然后你的 .env 文件长这样APP_NAMEFastAPI配置管理测试项目 DATABASE_URLpostgresql://user:passlocalhost/db SECRET_KEYsuper-secret-key DEBUGfalse你注意到没字段名大小写自动匹配而且 debug 直接当布尔值用不用自己 int(0) 转换。pydantic 还会帮你做校验万一环境变量里没设 database_url 应用启动时直接抛错避免带着空配置跑起来。是不是以为这样就完了还不够。生产环境的配置通常会覆盖某些默认值这时你可以这么做class Settings(BaseSettings): app_name: str MyApp debug: bool False class Config: env_file .env # 当存在 .env.prod 时优先加载 env_file .env.prod if os.getenv(ENV) prod else .env官方文档虽然没强制但根据我的经验最好把不同环境的 .env 文件分开再用环境变量ENV来决定加载哪个。 .env 文件放哪里不是无所谓但也很灵活既然 Settings 类已经在用了那 .env 文件到底该扔在哪儿绝对是个“说大不大说小坑一堆”的问题。好咱们把它掰扯清楚。默认情况下pydantic_settings 会在当前工作目录CWD下找你的 .env 文件。也就是说你在哪个目录执行的 Python 命令它就去哪个目录找。举个例子如果你的项目结构长这样my_project/ ├── app/ │ ├── main.py │ └── config.py ├── .env └── requirements.txt你在 my_project 目录下执行python app/main.py或者uvicorn app.main:app当前工作目录就是 my_project所以它能找到同级的 .env稳稳的。但是一旦你换个姿势比如在别的地方执行或者用 Docker、systemd 启动工作目录往往就跑偏了。我也曾经因为这个问题在服务器上排查了一下午配置死活不生效最后发现是 supervisor 把工作目录切到了 /root…… 最佳实践显式指定路径不依赖 CWD别再猜来猜去了直接在 Settings 类的 Config 里把路径写清楚。这里有一个我屡试不爽的写法from pydantic_settings import BaseSettings import os class Settings(BaseSettings): database_url: str secret_key: str class Config: # 关键用 file 定位到 config.py 同级目录下的 .env env_file os.path.join(os.path.dirname(__file__), .env) env_file_encoding utf-8这么一来不管你在哪里执行它永远去找 config.py 所在目录下的 .env 文件。也就是说把 .env 和 config.py 放在同一个文件夹里通常是 app 目录下就再也不会丢了。你可能会问“那多环境怎么办比如 .env.prod 放哪儿” 同样啊也放 config.py 旁边然后根据环境变量稍微改一下路径就行import os env os.getenv(APP_ENV, dev) env_file os.path.join(os.path.dirname(__file__), f.env.{env}) 查找机制的底层逻辑一句话版如果你不显式指定路径pydantic_settings 内部其实调用的是 python-dotenv 的load_dotenv(env_file)它就会基于当前工作目录去解析相对路径。所以这玩意儿就像“前台的快递”你得站在正确的工位上才能收到。而用__file__的方式则相当于你把收货地址写成了“我家”无论快递员从哪个邮局出发都能送到。 几个血的教训✖️ 不要放在项目的上级目录否则部署时很容易漏掉。✖️ 不要放在会随代码一起被静态文件服务暴露的目录下比如 static/别人直接通过 URL 就可能下载到。✔️ 一律放在项目代码包内部和 config.py 做邻居再配合 .gitignore最安全。 第四部分配置优先级 —— 别被覆盖规则搞晕这里有个很容易踩的坑。 pydantic_settings 加载配置的优先级是这样的 系统环境变量最高优先级 .env 文件中的值 你在类里设置的默认值也就是说哪怕 .env 里有 DATABASE_URL 如果你在启动容器时传入了同名环境变量它会被覆盖。这其实是个好事特别适合在 k8s 或 docker-compose 里注入密钥而不改动镜像。 在路由里用上你的 Settings直接上最简单粗暴的办法——导入实例当普通对象用。