跳转至

Pydantic Settings 配置管理

为什么要学 Pydantic Settings

Pydantic Settings 是一个基于 Pydantic 的应用配置管理库。它能从环境变量、.env 文件、JSON/YAML 配置文件等多种来源加载配置,并自动进行类型验证和转换。与手动解析 os.environ 相比,Pydantic Settings 提供了类型安全、自动校验、IDE 补全和文档化的配置管理方式。对于任何 Python 应用的配置管理来说,这是最现代和最可靠的方案。


核心概念

概念白话解释用途
BaseSettings设置基类所有配置类的父类
Environment Variables环境变量从系统环境读取配置
.env File环境文件从文件加载配置
Validators验证器自定义验证逻辑
Nested Models嵌套模型结构化的配置组
Field字段配置项定义

安装配置

pip install pydantic-settings

# 支持 .env 文件
pip install pydantic-settings[dotenv]

# 支持 YAML/TOML
pip install pydantic-settings[yaml,toml]

快速上手

基本使用

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "MyApp"
    debug: bool = False
    database_url: str
    redis_url: str = "redis://localhost:6379"
    api_key: str

    model_config = {
        "env_file": ".env",
        "env_file_encoding": "utf-8",
    }

# .env 文件:
# DATABASE_URL=postgresql://user:pass@localhost/db
# API_KEY=sk-xxx

settings = Settings()
print(settings.database_url)  # postgresql://user:pass@localhost/db
print(settings.debug)         # False

类型自动转换

class Settings(BaseSettings):
    port: int = 8000            # "8000" → 8000
    debug: bool = False         # "true" → True
    hosts: list[str] = []      # '["a","b"]' → ["a", "b"]
    timeout: float = 30.0       # "30.0" → 30.0

    model_config = {"env_file": ".env"}

嵌套配置

from pydantic import BaseModel

class DatabaseSettings(BaseModel):
    host: str = "localhost"
    port: int = 5432
    name: str = "mydb"
    user: str = "admin"
    password: str

class RedisSettings(BaseModel):
    host: str = "localhost"
    port: int = 6379
    db: int = 0

class Settings(BaseSettings):
    app_name: str = "MyApp"
    database: DatabaseSettings
    redis: RedisSettings

    model_config = {
        "env_nested_delimiter": "__",
        "env_file": ".env",
    }

# 环境变量:
# DATABASE__HOST=db.example.com
# DATABASE__PASSWORD=secret
# REDIS__PORT=6380

进阶用法

自定义验证

from pydantic import field_validator, model_validator

class Settings(BaseSettings):
    database_url: str
    api_key: str
    log_level: str = "INFO"

    @field_validator("log_level")
    @classmethod
    def validate_log_level(cls, v):
        allowed = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
        if v.upper() not in allowed:
            raise ValueError(f"log_level must be one of {allowed}")
        return v.upper()

    @field_validator("api_key")
    @classmethod
    def validate_api_key(cls, v):
        if not v.startswith("sk-"):
            raise ValueError("API key must start with 'sk-'")
        return v

    @model_validator(mode="after")
    def validate_model(self):
        if self.log_level == "DEBUG" and "prod" in self.database_url:
            raise ValueError("Cannot use DEBUG log level with production database")
        return self

多环境配置

from pydantic_settings import BaseSettings
from typing import Literal

class Settings(BaseSettings):
    environment: Literal["development", "staging", "production"] = "development"
    debug: bool = False
    database_url: str

    model_config = {
        "env_file": (".env", ".env.local"),  # 多文件,后者覆盖前者
    }

class DevSettings(Settings):
    debug: bool = True
    database_url: str = "sqlite:///dev.db"

class ProdSettings(Settings):
    debug: bool = False

    model_config = {
        "env_file": ".env.production",
    }

def get_settings():
    env = os.getenv("ENVIRONMENT", "development")
    if env == "production":
        return ProdSettings()
    return DevSettings()

YAML/TOML 配置源

from pydantic_settings import BaseSettings, YamlConfigSettingsSource

class Settings(BaseSettings):
    app_name: str
    debug: bool = False

    @classmethod
    def settings_customise_sources(cls, settings_cls, **kwargs):
        return (
            # 优先级从高到低
            kwargs.get("env_settings", ()),
            kwargs.get("dotenv_settings", ()),
            YamlConfigSettingsSource(settings_cls, yaml_file="config.yaml"),
            kwargs.get("init_settings", ()),
        )

FastAPI 集成

from fastapi import FastAPI, Depends
from functools import lru_cache

class Settings(BaseSettings):
    app_name: str = "My API"
    admin_email: str
    database_url: str

    model_config = {"env_file": ".env"}

@lru_cache
def get_settings():
    return Settings()

app = FastAPI()

@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
    return {
        "app_name": settings.app_name,
        "admin_email": settings.admin_email,
    }

敏感字段

from pydantic import SecretStr

class Settings(BaseSettings):
    api_key: SecretStr
    database_password: SecretStr

    model_config = {"env_file": ".env"}

settings = Settings()
print(settings.api_key)                    # SecretStr('**********')
print(settings.api_key.get_secret_value()) # 实际值
print(settings.model_dump())               # api_key 显示为 **********

常见问题

Q: 环境变量名的规则?

默认将字段名转为大写作为环境变量名。可通过 env_prefix 添加前缀:

model_config = {"env_prefix": "MYAPP_"}
# MYAPP_DATABASE_URL, MYAPP_API_KEY 等

Q: 配置优先级是什么?

默认优先级(高到低): 1. __init__ 参数 2. 环境变量 3. .env 文件 4. 默认值

Q: 如何在测试中覆盖配置?

def test_something():
    settings = Settings(database_url="sqlite:///test.db", api_key="sk-test")
    # 或
    with mock.patch.dict(os.environ, {"DATABASE_URL": "sqlite:///test.db"}):
        settings = Settings()

参考资源

  • 文档:https://docs.pydantic.dev/latest/concepts/pydantic_settings/
  • GitHub:https://github.com/pydantic/pydantic-settings
  • Pydantic 文档:https://docs.pydantic.dev/
  • FastAPI 配置:https://fastapi.tiangolo.com/advanced/settings/