Pydantic配置管理最佳实践(一)

📅 今日知识点

  • 核心主题:使用Pydantic管理应用配置、环境变量解析、配置验证、多环境配置切换
  • 适用场景:Web应用配置管理、微服务配置中心、CLI工具参数管理、十二要素应用配置实践
  • 一句话总结:Pydantic配置管理让配置验证自动化,环境变量无缝集成,告别配置错误和类型问题

🧩 核心原理(简化版)

  • Pydantic的BaseSettings类专为配置管理设计,自动从环境变量加载配置
  • 核心逻辑:定义配置模型类,并使其继承自BaseSettings,其字段便会自动映射环境变量,内置类型验证和默认值处理
  • 核心价值:统一配置入口、自动类型转换、环境隔离、配置文档自动生成

💻 代码实战

1. 安装依赖

bash 复制代码
pip install pydantic[dotenv]  # 包含dotenv支持

2. 环境配置文件

env 复制代码
# 创建环境文件.env,包含以下内容:
APP_NAME=MyAwesomeApp1
DEBUG=true
VERSION=1.0.1
API_KEYS=["key1","key2","key3"]

DB_HOST=127.0.0.1
DB_PORT=54321
DB_USERNAME=user
DB_PASSWORD=pass
DB_NAME=pass

REDIS_HOST=127.0.0.1
REDIS_PORT=6371
REDIS_PASSWORD=123456
REDIS_DB=0

3. 基础配置管理类示例

python 复制代码
from pathlib import Path
from typing import List, Optional
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings

# 数据库配置
class DatabaseSettings(BaseSettings):
    """数据库配置"""

    host: str = Field(default="localhost", alias="DB_HOST")
    port: int = Field(default=5432, alias="DB_PORT")
    # Make individual parts optional; allow providing a full DATABASE_URL instead
    username: Optional[str] = Field(default=None, alias="DB_USERNAME")
    password: Optional[str] = Field(default=None, alias="DB_PASSWORD")
    database: Optional[str] = Field(default=None, alias="DB_NAME")
    database_url: Optional[str] = Field(default=None, alias="DATABASE_URL")

    # 数据库URL构建
    @property
    def url(self) -> str:
        # If a full DATABASE_URL was provided, prefer that
        if self.database_url:
            return self.database_url
        # Fallback to building from parts (use empty strings for missing values)
        user = self.username or ""
        pwd = self.password or ""
        db = self.database or ""
        return f"postgresql://{user}:{pwd}@{self.host}:{self.port}/{db}"

    model_config = {
        "env_prefix": "DB_",  # 环境变量前缀
        "extra": "ignore",  # 忽略未定义字段
        "case_sensitive": False,  # 环境变量不区分大小写
        "env_file": ".env",  # 环境变量文件配置
        "env_file_encoding": "utf-8"
    }

# Redis配置
class RedisSettings(BaseSettings):
    """Redis配置"""
    host: str = "localhost"
    port: int = 6379
    password: Optional[str] = None
    db: int = 0

    model_config = {
        "env_prefix": "REDIS_",
        "extra": "ignore",  # 忽略未定义字段
        "case_sensitive": False,  # 环境变量不区分大小写
        "env_file": ".env",  # 环境变量文件配置
        "env_file_encoding": "utf-8"
    }

# 包含数据库和redis配置的主应用配置
class AppSettings(BaseSettings):
    """应用主配置"""
    app_name: str = "MyApp"
    debug: bool = False
    version: str = "1.0.0"

    # 嵌套配置 - 使用工厂函数延迟初始化
    database: DatabaseSettings = Field(default_factory=DatabaseSettings)
    redis: RedisSettings = Field(default_factory=RedisSettings)

    # 复杂类型配置
    # Store the raw env value (string) and parse it into a Python list via a
    # property. This avoids pydantic-settings attempting to JSON-decode a
    # List[str] field from a comma-separated .env value.
    api_keys: Optional[str] = Field(default=None, alias="API_KEYS")
    allowed_hosts: List[str] = ["localhost"]
    log_level: str = "INFO"

    # 路径配置
    data_dir: Path = Field(default=Path("./data"))
    temp_dir: Path = Field(default=Path("./temp"))

    # 自定义验证
    @field_validator('log_level')
    @classmethod
    def validate_log_level(cls, v):
        # Ensure we accept None or empty values and normalize to upper-case
        if v is None:
            return "INFO"

        if isinstance(v, str):
            valid_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
            if v.upper() not in valid_levels:
                raise ValueError(f'日志级别必须是: {valid_levels}')
            return v.upper()
        raise ValueError('log_level must be a string')

    model_config = {
        # 环境变量文件配置
        "env_file": ".env",
        "env_file_encoding": "utf-8",
        
        # 允许通过字段名而不是别名填充
        "case_sensitive": False,
        "extra": "ignore"  # 忽略未定义字段
    }

4. 测试

python 复制代码
def main():
    try:
        # 加载配置
        settings = AppSettings()

        print("✅ 配置加载成功!")
        print(f"应用名称: {settings.app_name}")
        print(f"调试模式: {settings.debug}")
        print(f"数据库URL: {settings.database.url}")
        print(f"Redis地址: {settings.redis.host}:{settings.redis.port}")
        print(f"API密钥数量: {len(settings.api_keys)}")
        print(f"API密钥列表: {settings.api_keys}")
        print(f"数据目录: {settings.data_dir.absolute()}")
        print(f"日志级别: {settings.log_level}")

        # 验证路径存在
        settings.data_dir.mkdir(exist_ok=True)
        settings.temp_dir.mkdir(exist_ok=True)
        print("📁 目录创建完成")
    except Exception as e:
        import traceback
        print(f"❌ 配置加载失败: {e!r}")
        traceback.print_exc()
        print("请检查:")
        print("1. .env 文件是否存在且格式正确")
        print("2. 必需的环境变量是否已设置")
        print("3. API_KEYS 是否为逗号分隔的字符串")

if __name__ == "__main__":
    main()

运行结果:

text 复制代码
✅ 配置加载成功!
应用名称: MyAwesomeApp1
调试模式: True
数据库URL: postgresql://user:pass@127.0.0.1:54321/pass
Redis地址: 127.0.0.1:6371
API密钥数量: 22
API密钥列表: ["key1","key2","key3"]
数据目录: D:\Gitee\TechLearn\learn-python\code\basic\data
日志级别: INFO
📁 目录创建完成

✅ 今日总结

  • Pydantic配置管理核心是"声明即配置",通过类型系统自动完成验证和转换
  • 关键要点:BaseSettings继承、环境变量映射、嵌套配置、类型安全
  • 实战价值:统一配置管理、减少配置错误
相关推荐
阿尔的代码屋7 小时前
[大模型实战 07] 基于 LlamaIndex ReAct 框架手搓全自动博客监控 Agent
人工智能·python
AI探索者1 天前
LangGraph StateGraph 实战:状态机聊天机器人构建指南
python
AI探索者1 天前
LangGraph 入门:构建带记忆功能的天气查询 Agent
python
FishCoderh1 天前
Python自动化办公实战:批量重命名文件,告别手动操作
python
躺平大鹅1 天前
Python函数入门详解(定义+调用+参数)
python
曲幽1 天前
我用FastAPI接ollama大模型,差点被asyncio整崩溃(附对话窗口实战)
python·fastapi·web·async·httpx·asyncio·ollama
两万五千个小时1 天前
落地实现 Anthropic Multi-Agent Research System
人工智能·python·架构
哈里谢顿1 天前
Python 高并发服务限流终极方案:从原理到生产落地(2026 实战指南)
python
用户8356290780512 天前
无需 Office:Python 批量转换 PPT 为图片
后端·python