Skip to content

配置管理

数据库地址、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-settings
python
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 自动转成布尔值 Truetoken_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=false

Settingsenv_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

必填配置(没默认值的字段)在启动时强制校验——少了一个应用就起不来,比运行到某个接口才发现密钥缺失安全得多。