如何利用Pydantic特性从嵌套字典提取架构至指定子模型?
解决方案:用Pydantic特性替代
__post_init__手动赋值 首先得提个小问题:你的输入数据里databases是字典结构(键为service_db和other_db),但原代码里把databases定义成了List类型,这会导致解析失败,所以第一步要修正类型定义为Dict[str, Union[ServiceDatabase, OtherDatabase]],匹配输入数据的实际结构。
接下来,我们可以用Pydantic的特性实现你想要的「自动从指定字段提取子模型」的效果,不需要再依赖__post_init__手动赋值。下面分两种常用场景给出方案:
方案一:用Pydantic v2的计算字段(推荐)
如果你的项目使用Pydantic v2,@computed_field是最优雅的解决方案,它会自动在实例化时计算出目标属性:
from dataclasses import asdict from typing import Dict, Union from pydantic.dataclasses import dataclass from pydantic import computed_field @dataclass class ServiceDatabase: connect_string: str @dataclass class OtherDatabase: connect_string: str service: str @dataclass class PydConfigurator: databases: Dict[str, Union[ServiceDatabase, OtherDatabase]] @computed_field def service_db(self) -> ServiceDatabase: # 直接从databases字典中提取对应键的配置,Pydantic会自动做类型校验 return self.databases["service_db"] @computed_field def other_db(self) -> OtherDatabase: return self.databases["other_db"] data = { "databases": { "service_db": { "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}" }, "other_db": { "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}", "service": "Any" } } } if __name__ == '__main__': config = PydConfigurator(**data) assert asdict(config.service_db) == data['databases']['service_db'] assert asdict(config.other_db) == data['databases']['other_db'] print("所有断言通过!")
关键说明
@computed_field装饰器告诉Pydantic:这两个属性是基于已有字段计算得到的,不需要用户传入,会在实例化时自动生成。- 类型安全:Pydantic会自动校验提取的值是否符合目标类型(比如
service_db必须是ServiceDatabase实例),如果类型不匹配会直接抛出验证错误,比手动赋值更可靠。
方案二:用Pydantic验证器(兼容v1)
如果你的项目还在使用Pydantic v1,或者更习惯用验证器的方式,可以通过@validator实现自动提取:
from dataclasses import asdict from typing import Dict, Union from pydantic.dataclasses import dataclass from pydantic import validator @dataclass class ServiceDatabase: connect_string: str @dataclass class OtherDatabase: connect_string: str service: str @dataclass class PydConfigurator: databases: Dict[str, Union[ServiceDatabase, OtherDatabase]] service_db: ServiceDatabase = None other_db: OtherDatabase = None @validator("service_db", pre=True, always=True) def extract_service_db(cls, v, values): # pre=True:在其他验证逻辑前执行;always=True:即使没传入参数也会执行 return values["databases"]["service_db"] @validator("other_db", pre=True, always=True) def extract_other_db(cls, v, values): return values["databases"]["other_db"] data = { "databases": { "service_db": { "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}" }, "other_db": { "connect_string": "postgresql://{user}:{password}@{host}:{port}/{db_name}", "service": "Any" } } } if __name__ == '__main__': config = PydConfigurator(**data) assert asdict(config.service_db) == data['databases']['service_db'] assert asdict(config.other_db) == data['databases']['other_db'] print("所有断言通过!")
内容的提问来源于stack exchange,提问作者Sergey Gavrilov
相关产品推荐
相关产品推荐

