配置管理
本教程共 50 篇 · 第 44 篇 · 更新于 2026-08-12 · 约 8 分钟阅读
本节目标:学会用 pydantic-settings 把数据库地址、密钥等配置从代码里抽出来,通过环境变量和 .env 文件管理,并能区分开发环境和生产环境。
应用里总有些值会变,而且不该写死在代码里。比如数据库地址、邮件服务的账号密码、一个用于签名的密钥。这些值既可能随环境变化,又往往很敏感。把它们写进代码再提交到仓库,既不安全也不灵活。标准做法是:把配置放在「环境变量」里,让程序启动时读出来。
44-1 环境变量是什么
环境变量(env var)是活在 Python 代码之外、由操作系统保管的一组键值。你的程序和别的程序都能读到它。它的特点是只能存字符串——因为操作系统层面不认识 Python 的 int、bool 这些类型。
这就带来一个问题:环境变量里读出来的都是 str,要变成 int、做校验,得自己写代码转换。pydantic-settings 正好帮我们解决了这件事。
44-2 安装 pydantic-settings
pydantic-settings 不是 FastAPI 自带的核心包,需要单独装:
pip install pydantic-settings
裸 pip install fastapi 不会包含 pydantic-settings。但本书第 3 章装的 fastapi[standard] 已自带它(官方从 0.116 起把它并入 standard 组),所以跟着前面装的读者无需再装。若不确定,显式执行上面的 pip install pydantic-settings 也无害。
44-3 创建 Settings 类
用法和 Pydantic 模型几乎一样。从 pydantic_settings 导入 BaseSettings,然后像定义模型那样声明字段和类型:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "默认应用名"
admin_email: str = "admin@example.com"
items_per_user: int = 50
settings = Settings()
创建 Settings() 实例时,pydantic-settings 会自动去读环境变量,而且是「大小写不敏感」的。也就是说环境变量写成 APP_NAME 或 app_name 都能被 app_name 字段读到。
接着它会做类型转换和校验。比如 items_per_user 在环境变量里是字符串 "100",读进来后会被转成 int 类型的 100。没提供的字段就保留默认值。
44-4 在应用里使用配置
拿到 settings 对象后,就能像普通对象一样用了:
from fastapi import FastAPI
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "默认应用名"
admin_email: str = "admin@example.com"
settings = Settings()
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
}
启动服务器时,把配置当作环境变量传进去:
# Linux / macOS / Windows Bash
ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
# Windows PowerShell
$Env:ADMIN_EMAIL = "deadpool@example.com"
$Env:APP_NAME = "ChimichangApp"
fastapi run main.py
这样 app_name 就变成 ChimichangApp,admin_email 变成 deadpool@example.com,而 items_per_user 保持默认 50。
TipBash 里多个环境变量要写在同一行、放在命令前面,用空格隔开。每条
key=value之间也是空格,不要加分号。
44-5 把配置放进独立模块
项目稍大时,建议把 Settings 单独放到 config.py,主文件再导入,更清爽:
# config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "默认应用名"
admin_email: str = "admin@example.com"
settings = Settings()
# main.py
from fastapi import FastAPI
from config import settings
app = FastAPI()
@app.get("/")
async def root():
return {"app_name": settings.app_name}
若把 config.py 放在某个子包里,该目录需要有 __init__.py 才能被常规导入;放在项目根目录则可以直接 from config import settings(详见大型应用结构那一章)。
44-6 用依赖注入,方便测试
有些场景把 settings 做成全局对象不太灵活。更好的办法是用一个依赖函数返回 Settings(),需要时在路径操作里声明依赖:
# config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
admin_email: str = "admin@example.com"
app_name: str = "默认应用名"
# main.py
from functools import lru_cache
from fastapi import Depends, FastAPI
import config
@lru_cache
def get_settings() -> config.Settings:
return config.Settings()
app = FastAPI()
@app.get("/info")
async def info(settings: config.Settings = Depends(get_settings)):
return {"app_name": settings.app_name}
这样写的好处是测试时很容易替换配置,只要覆盖 get_settings 这个依赖即可(覆盖依赖的详细做法见测试章节)。
44-7 读取 .env 文件
环境变量太多、经常变,或要在多个环境间切换时,常见做法是把它们写进一个名为 .env 的文件(也叫 dotenv),程序启动时像读环境变量一样读它。
pydantic-settings 已把 python-dotenv 作为硬依赖一起装上,无需再单独安装。
.env 文件内容长这样:
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
然后在 Settings 类里用 model_config 指定文件名:
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
admin_email: str = "admin@example.com"
app_name: str = "默认应用名"
Note
model_config是 Pydantic v2 的写法。旧版教程里的class Config:是 v1 写法,不要再用。env_file告诉 pydantic 去哪个 dotenv 文件读变量。
44-8 用 lru_cache 只读取一次
读 .env 文件是磁盘操作,比较慢。如果每次请求都 Settings() 新建对象、重读文件,会浪费资源。给 get_settings 加 @lru_cache,它只会在第一次被调用时真正创建对象,之后都返回同一个:
from functools import lru_cache
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
admin_email: str = "admin@example.com"
@lru_cache
def get_settings() -> Settings:
return Settings()
@lru_cache 来自标准库 functools,会缓存函数的返回值。get_settings() 不带参数,所以永远返回同一份配置,既省了反复读文件,又能在测试里轻松覆盖。
44-9 多环境与敏感信息
多环境切换很简单:开发用一份 .env,生产用另一份环境变量。比如开发连本地库 sqlite:///dev.db,生产连远程库 postgresql://...。生产环境不要提交 .env 到仓库,把真正的密钥放在服务器环境变量里。
这里要理解 pydantic-settings 的取值优先级:真实环境变量高于 .env 文件,.env 又高于类里写的默认值。这个顺序正是多环境方案能成立的原因——生产服务器上只要注入同名环境变量,就能覆盖掉镜像里可能残留的 .env,不必改一行代码。字段名与环境变量名的对应默认不区分大小写,所以类里写 admin_email,环境变量写成 ADMIN_EMAIL 就能匹配上。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
database_url: str = "sqlite:///./dev.db"
secret_key: str = "请在生产环境用环境变量覆盖我"
settings = Settings()
Warning永远不要把数据库密码、API 密钥、签名用的
secret_key写进代码或提交到 Git 仓库。让它们在生产环境通过真实环境变量注入,.env只放本地开发值,并且把.env加进.gitignore。
44-10 一份可参考的完整示例
把前面几点合起来,一个较完整的 config.py 长这样:
from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
app_name: str = "默认应用名"
admin_email: str = "admin@example.com"
database_url: str = "sqlite:///./dev.db"
# 生产环境务必用环境变量覆盖下面这项
secret_key: str = "开发用占位密钥"
@lru_cache
def get_settings() -> Settings:
return Settings()
开发时读 .env,生产时由系统环境变量覆盖 database_url 和 secret_key。这样既本地方便、又线上安全,是社区推荐的基本做法。
44-11 小结
用 pydantic_settings.BaseSettings 管理配置,既支持类型校验,又能从环境变量和 .env 读取。把配置放进独立模块、用 get_settings 依赖注入,测试时易于替换。文件读取记得加 @lru_cache。敏感信息一律走环境变量,别进代码库。