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继承、环境变量映射、嵌套配置、类型安全
  • 实战价值:统一配置管理、减少配置错误
相关推荐
Draina2 小时前
CBC填充预言攻击-CBC Padding Oracle Crypto Attack
python·安全·web安全·网络安全·密码学·安全性测试
Angel Q.2 小时前
特征提取 | DINO 到 DINOv3
图像处理·python
不如语冰3 小时前
AI大模型入门-pytorch-张量2 tensor创建
python
NPE~3 小时前
[AI]Agent开发——ADK框架使用
人工智能·python·ai·教程·adk·agent开发
矮个史蒂芬3 小时前
统计verilog .v文件中output 的bit数
python
gwf2163 小时前
磨损均衡算法(Wear Leveling)——SSD如何让每块闪存“公平退休“?
运维·数据库·人工智能·python·嵌入式硬件·算法·智能硬件
爱吃提升3 小时前
python 分布式爬虫、爬虫合规与综合实战项目
分布式·爬虫·python
审小匠OpenCPAi3 小时前
银行流水核查怎么自动化?单边匹配、双向勾稽与图聚类异常检测的工程对比
java·前端·人工智能·python·审计
元Y亨H5 小时前
Pandas 解析 Excel 导致的内存溢出(MemoryError)
python·excel
金銀銅鐵5 小时前
[Python] 用 turtle 来绘制瑞典国旗
python