在项目中管理上百个配置项时,手写Mixin类分别做取值校验不仅重复劳动,还容易遗漏边界条件。既然项目本身已用FastAPI等框架引入了Pydantic,为什么不直接用Pydantic的模型校验来处理配置文件?本文基于TOML格式配置文件,展示如何用Pydantic代替传统Mixin校验,配合标准库tomllib实现类型安全、错误明确的配置加载方案。
## 安装依赖
仅需安装Pydantic,无需安装pydantic-settings。Python 3.11+自带tomllib用于解析TOML文件。- uv add -U pydantic
- # 或 pip:python -m pip install -U pydantic
复制代码
## 配置文件示例(conf/config.toml)- [service]
- host = "127.0.0.1"
- port = 8000
- env = "dev" # dev, prod
- [service.log]
- level = "DEBUG"
- output = "BOTH"
- dir = "logs"
- retention_days = 30
- colorize = true
- diagnose = true
- backtrace = true
- [database.postgres]
- host = "127.0.0.1"
- port = 5432
- user = "your_user"
- password = "your_password"
- dbname = "your_dbname"
- pool_max_size = 10
- pool_min_size = 4
复制代码
## 使用Pydantic定义配置模型
服务配置和日志配置(pkg/config/service.py):- from typing import Annotated, Literal
- from pydantic import BaseModel, Field
- class ServiceLogConfig(BaseModel):
- level: Annotated[Literal["DEBUG","INFO","WARNING","ERROR"], Field(default="INFO", description="日志级别")]
- dir: Annotated[str, Field(default="logs", description="日志文件目录")]
- output: Annotated[Literal["STDOUT","FILE","BOTH"], Field(default="STDOUT", description="日志输出方式")]
- retention_days: Annotated[int, Field(default=7, gt=0, le=30, description="日志文件轮转天数")]
- colorize: Annotated[bool, Field(default=True, description="彩色日志")]
- backtrace: Annotated[bool, Field(default=True, description="堆栈跟踪")]
- diagnose: Annotated[bool, Field(default=True, description="诊断日志")]
- class ServiceConfig(BaseModel):
- host: Annotated[str, Field(default="127.0.0.1", description="监听地址")]
- port: Annotated[int, Field(default=8080, description="端口")]
- env: Annotated[Literal["dev","prod"], Field(default="dev", description="环境")]
- log: ServiceLogConfig
复制代码
数据库配置(pkg/config/postgres.py),包含生成DSN的方法:- from typing import Annotated
- from urllib.parse import quote_plus
- from pydantic import BaseModel, Field
- class PostgresConfig(BaseModel):
- host: Annotated[str, Field(..., description="PostgreSQL host")]
- port: Annotated[int, Field(..., description="PostgreSQL port")]
- user: Annotated[str, Field(..., description="PostgreSQL user")]
- password: Annotated[str, Field(..., description="PostgreSQL password")]
- dbname: Annotated[str, Field(..., description="Database name")]
- pool_min_size: Annotated[int, Field(..., description="连接池最小连接数")]
- pool_max_size: Annotated[int, Field(..., description="连接池最大连接数")]
- def get_dsn(self) -> str:
- user = quote_plus(self.user)
- password = quote_plus(self.password)
- return f"postgresql://{user}:{password}@{self.host}:{self.port}/{self.dbname}"
- class DatabaseConfig(BaseModel):
- postgres: PostgresConfig
复制代码
## 组合为全局配置类并实现加载(pkg/config/config.py)- from pathlib import Path
- import tomllib
- from pydantic import BaseModel, ValidationError
- from .service import ServiceConfig
- from .postgres import DatabaseConfig
- class Config(BaseModel):
- service: ServiceConfig
- database: DatabaseConfig
- def get_config() -> Config:
- """加载并校验配置文件"""
- config_file = Path(__file__).parent.parent.parent / "conf" / "config.toml"
- with open(config_file, "rb") as f:
- raw_config = tomllib.load(f)
- try:
- # Pydantic v2推荐用model_validate进行严格校验
- return Config.model_validate(raw_config)
- except ValidationError as e:
- raise RuntimeError(f"配置校验失败: {e}") from e
复制代码
在pkg/config/__init__.py中实例化全局单例:- from .config import get_config
- cfg = get_config()
- __all__ = ["cfg"]
复制代码
调用方直接使用:- from pkg.config import cfg
- dsn = cfg.database.postgres.get_dsn()
复制代码
## 校验失败示例
如果配置文件的database.postgres.port被写为"5432qwer"(字符串),而模型声明为int型,启动时会抛出异常,错误信息明确指向具体字段:- RuntimeError: Failed to validate config: 1 validation error for Config
- database.postgres.port
- Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='5432qwer', input_type=str]
- For further information visit https://errors.pydantic.dev/2.13/v/int_parsing
复制代码 这种方式比手动编写if-else校验更简单可靠,复杂模型嵌套时的错误定位也一目了然。对于JSON、YAML等其他格式配置,只需将解析结果(字典)传给model_validate即可,代码逻辑完全一致。 |