Python3.11+Pydantic2.5:软件版本校验与缺省字段填充问题
问题描述
使用Python 3.11和Pydantic 2.5搭建数据模型,现有配置数据:
data = { 'project': 'abc', 'id': 12345, 'software': { 'aaa': { 'version': '20.1.2.2013', 'path': '//path_to_software/aaa' }, 'bbb': { 'version': '4.0.2', 'path': '//path_to_software/bbb' }, 'ccc': { 'version': '10.0v1', 'path': '//path_to_software/ccc' } } }
对应的现有Pydantic模型:
import pydantic import pathlib class SoftwareVersion(pydantic.BaseModel): version: str path: pathlib.Path class Software(pydantic.BaseModel): aaa: SoftwareVersion bbb: SoftwareVersion ccc: SoftwareVersion class Project(pydantic.BaseModel): project: str id: int software: Software
目前模型运行正常,但有两个需求:
- 为每个软件的
version字段做格式校验,无需为每个软件单独创建版本类,希望在SoftwareVersion或Software类中实现校验。 - 当软件字段(比如
ddd)不存在时,自动创建该字段并填充默认值。
解决方案
1. 版本格式校验(在SoftwareVersion类中统一实现)
直接在SoftwareVersion类中给version字段添加校验器,所有引用该类的软件字段都会自动继承校验规则,无需单独为每个软件建类。使用Pydantic的@field_validator装饰器实现:
import pydantic import pathlib from pydantic import field_validator import re class SoftwareVersion(pydantic.BaseModel): version: str path: pathlib.Path @field_validator('version') def validate_version_format(cls, v): # 示例正则支持三种版本格式:x.y.z.build、x.y.z、x.yvx version_pattern = r'^(?:\d+\.){2}\d+\.\d+$|^(?:\d+\.){2}\d+$|^\d+\.\d+v\d+$' if not re.match(version_pattern, v): raise ValueError(f"无效的版本格式:{v},支持格式如20.1.2.2013、4.0.2、10.0v1") return v class Software(pydantic.BaseModel): aaa: SoftwareVersion bbb: SoftwareVersion ccc: SoftwareVersion class Project(pydantic.BaseModel): project: str id: int software: Software
无论aaa、bbb还是其他软件字段,只要使用SoftwareVersion类型,都会自动校验version格式。
2. 自动创建缺失字段并填充默认值
根据软件字段是否固定,提供两种实现方式:
方式一:固定字段直接设置默认值
如果已知所有可能的软件字段,直接在Software类中为每个字段定义默认值:
class Software(pydantic.BaseModel): aaa: SoftwareVersion = SoftwareVersion(version='1.0.0', path=pathlib.Path('//default/path/aaa')) bbb: SoftwareVersion = SoftwareVersion(version='1.0.0', path=pathlib.Path('//default/path/bbb')) ccc: SoftwareVersion = SoftwareVersion(version='1.0.0', path=pathlib.Path('//default/path/ccc')) ddd: SoftwareVersion = SoftwareVersion(version='1.0.0', path=pathlib.Path('//default/path/ddd'))
数据中缺少对应字段时,会自动使用定义的默认值。
方式二:动态字段结合模型校验器填充
如果软件字段是动态不确定的,可通过model_config允许额外字段,并使用model_validator自动填充缺失值:
from pydantic import model_validator class Software(pydantic.BaseModel): aaa: SoftwareVersion bbb: SoftwareVersion ccc: SoftwareVersion model_config = {'extra': 'allow'} # 允许定义外的软件字段 @model_validator(mode='before') def fill_missing_software(cls, values): # 定义所有需要默认值的软件字段及默认配置 default_software = { 'aaa': {'version': '1.0.0', 'path': '//default/path/aaa'}, 'bbb': {'version': '1.0.0', 'path': '//default/path/bbb'}, 'ccc': {'version': '1.0.0', 'path': '//default/path/ccc'}, 'ddd': {'version': '1.0.0', 'path': '//default/path/ddd'} } # 合并输入值与默认值,输入值优先级更高 for key, default_val in default_software.items(): if key not in values: values[key] = default_val return values
该方式既自动填充缺失字段的默认值,也支持新增未预先定义的软件字段。
组合方案(同时实现校验与默认值填充)
将上述两种方案结合,完整代码如下:
import pydantic import pathlib from pydantic import field_validator, model_validator import re class SoftwareVersion(pydantic.BaseModel): version: str path: pathlib.Path @field_validator('version') def validate_version_format(cls, v): version_pattern = r'^(?:\d+\.){2}\d+\.\d+$|^(?:\d+\.){2}\d+$|^\d+\.\d+v\d+$' if not re.match(version_pattern, v): raise ValueError(f"无效的版本格式:{v},支持格式如20.1.2.2013、4.0.2、10.0v1") return v class Software(pydantic.BaseModel): aaa: SoftwareVersion bbb: SoftwareVersion ccc: SoftwareVersion model_config = {'extra': 'allow'} @model_validator(mode='before') def fill_missing_software(cls, values): default_software = { 'aaa': {'version': '1.0.0', 'path': '//default/path/aaa'}, 'bbb': {'version': '1.0.0', 'path': '//default/path/bbb'}, 'ccc': {'version': '1.0.0', 'path': '//default/path/ccc'}, 'ddd': {'version': '1.0.0', 'path': '//default/path/ddd'} } for key, default_val in default_software.items(): if key not in values: values[key] = default_val return values class Project(pydantic.BaseModel): project: str id: int software: Software # 测试缺失字段场景 test_data = { 'project': 'abc', 'id': 12345, 'software': { 'aaa': { 'version': '20.1.2.2013', 'path': '//path_to_software/aaa' } } } project = Project(**test_data) print(project.model_dump()) # 输出会包含bbb、ccc、ddd的默认值,且所有version字段均经过格式校验
内容的提问来源于stack exchange,提问作者user1863947
相关产品推荐
相关产品推荐

