Appearance
配置管理
数据库地址、JWT 密钥、调试开关这些值,开发环境和生产环境不一样。硬编码在代码里——换环境要改代码、密钥进版本库泄露。正确的做法是把配置放在代码外面:环境变量和配置文件。
一、问题:配置散落在代码里
前面的代码里,密钥和数据库地址都是写死的:
python
# 这样不行——换环境要改代码,密钥泄露
SECRET_KEY = "your-secret-key-change-in-production"
engine = create_engine("sqlite:///articles.db")开发用 SQLite、生产用 MySQL,密钥也完全不同。配置应该从环境读取,而不是写死在代码里。
二、pydantic-settings
pydantic-settings 是 Pydantic 的配置管理扩展——用类型标注的类描述所有配置,自动从环境变量和 .env 文件读取:
bash
uv add pydantic-settingspython
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
"""应用配置。从环境变量和 .env 文件读取。"""
# 数据库
database_url: str = "sqlite:///articles.db"
# JWT
secret_key: str = "dev-secret-change-me"
token_expire_hours: int = 24
# 应用
debug: bool = False
app_name: str = "文章管理 API"
model_config = {
"env_file": ".env",
"env_file_encoding": "utf-8",
}
settings = Settings()Settings 类的每个属性就是一项配置——带类型标注,带默认值。model_config 里的 env_file 告诉它从 .env 文件读取。
用的时候直接引用:
python
engine = create_engine(settings.database_url)
SECRET_KEY = settings.secret_key类型标注不是摆设——debug 标注成 bool,环境变量 DEBUG=true 自动转成布尔值 True;token_expire_hours 标注成 int,环境变量 TOKEN_EXPIRE_HOURS=48 自动转成整数。不用手动 int() 或判断 "true" == "true"。
三、.env 文件
项目根目录建一个 .env 文件,写环境特定的配置:
ini
# .env
DATABASE_URL=postgresql://appuser:s3cret@localhost:5432/articles
SECRET_KEY=production-super-secret-key-generated-by-openssl
TOKEN_EXPIRE_HOURS=12
DEBUG=false环境变量名跟 Settings 的属性名对应,不区分大小写(DATABASE_URL 对应 database_url)。
.env 不能进版本库
.env 里有密钥和数据库密码,绝对不能进 Git。.gitignore 里加一行:
gitignore
.env进版本库的是 .env.example(示例文件,不含真实值):
ini
# .env.example —— 复制后改名为 .env,填入真实值
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
SECRET_KEY=请用 openssl rand -hex 32 生成
TOKEN_EXPIRE_HOURS=24
DEBUG=true新人拿到代码后复制 .env.example 为 .env,填入自己的值。
生成安全的密钥
bash
# 生成一个足够随机的密钥
openssl rand -hex 32
# 输出类似:a3f5e8b2c1d4f7a9e6b3c8d1f4a7b9e2c5d8f1a4b7e9c2d5f8a1b4e7c9d2f5a8密钥要足够长(至少 32 字节)且足够随机——用 openssl rand 生成的,不是手敲的"123456"。
四、多环境配置
开发、测试、生产三套环境,配置各不同。两种方式:
方式一:不同的 .env 文件
ini
# .env.dev
DATABASE_URL=sqlite:///articles.db
DEBUG=true
# .env.prod
DATABASE_URL=postgresql://appuser:xxx@db-host:5432/articles
DEBUG=falseSettings 的 env_file 根据环境变量 APP_ENV 动态选择:
python
import os
env = os.environ.get("APP_ENV", "dev")
class Settings(BaseSettings):
database_url: str = "sqlite:///articles.db"
debug: bool = False
model_config = {"env_file": f".env.{env}"}启动时指定环境:APP_ENV=prod uvicorn main:app。
方式二:系统环境变量覆盖
生产环境通常不依赖 .env 文件,直接用系统环境变量(Docker、Kubernetes 部署时从平台注入):
bash
export DATABASE_URL="postgresql://..."
export SECRET_KEY="..."
export DEBUG=false
uvicorn main:app系统环境变量的优先级高于 .env 文件——本地开发用 .env 文件方便,生产用系统环境变量安全。pydantic-settings 自动处理优先级:系统环境变量 > .env 文件 > 代码默认值。
五、配置校验
Settings 继承自 Pydantic,自带校验。配置值类型不对或缺少必填项,启动时报错——不用等运行到一半才发现配置有问题:
python
class Settings(BaseSettings):
database_url: str # 没有默认值 = 必填
secret_key: str # 必填
port: int = 8000
settings = Settings()
# 如果没设 DATABASE_URL 和 SECRET_KEY,启动时直接报错:
# pydantic_core.ValidationError: 2 validation errors for Settings
# database_url: Field required
# secret_key: Field required必填配置(没默认值的字段)在启动时强制校验——少了一个应用就起不来,比运行到某个接口才发现密钥缺失安全得多。