如何在Pydantic子模型验证中使用父模型的属性?
在Pydantic子模型验证中访问父模型属性的方案
问题场景
给定以下JSON数据:
{ "multiplier": 5, "field_1": { "value": 1 }, "field_2": { "value": 2 } }
对应的Pydantic模型定义如下:
from pydantic import BaseModel, validator class Item(BaseModel): value: int class Container(BaseModel): multiplier: int field_1: Item field_2: Item
需求是:在验证Item的value字段时,需要使用父模型Container的multiplier属性进行逻辑校验或计算,同时避免在父层编写复杂的嵌套验证逻辑,也不在子模型中重复定义multiplier字段。
解决方案
直接在Item的普通字段验证器中无法直接获取父模型的属性,因为子模型验证上下文默认不包含父模型信息。以下是几种可行的实现方式:
方法1:通过验证上下文传递参数(Pydantic v1)
在父模型的根验证器中,将multiplier作为上下文传递给子模型,子模型的验证器通过kwargs获取上下文参数:
from pydantic import BaseModel, validator, root_validator class Item(BaseModel): value: int @validator("value") @classmethod def validate_value(cls, value, **kwargs): # 从上下文获取父模型的multiplier,默认值为1 multiplier = kwargs.get('context', {}).get('multiplier', 1) # 示例验证逻辑:value * multiplier不能超过30 if value * multiplier > 30: raise ValueError(f"value * multiplier 不能超过30") # 返回计算后的值 return value * multiplier class Container(BaseModel): multiplier: int field_1: Item field_2: Item @root_validator(pre=True) @classmethod def pass_multiplier_to_items(cls, values): multiplier = values.get('multiplier') if multiplier is not None: # 为每个Item字段传递包含multiplier的上下文 for field_name in ['field_1', 'field_2']: if field_name in values: values[field_name] = Item(**values[field_name], context={'multiplier': multiplier}) return values # 测试示例 data = { "multiplier": 5, "field_1": {"value": 1}, "field_2": {"value": 2} } container = Container(**data) print(container.field_1.value) # 输出5 print(container.field_2.value) # 输出10
方法2:利用Pydantic v2的模型验证器传递上下文
如果使用Pydantic v2,可以通过model_validator和ValidationInfo来传递上下文参数:
from pydantic import BaseModel, field_validator, model_validator from pydantic_core import ValidationInfo class Item(BaseModel): value: int @field_validator('value') @classmethod def validate_value(cls, v, info: ValidationInfo): # 从上下文获取multiplier multiplier = info.context.get('multiplier', 1) if v * multiplier > 30: raise ValueError(f"value * multiplier 不能超过30") return v * multiplier class Container(BaseModel): multiplier: int field_1: Item field_2: Item @model_validator(mode='before') @classmethod def inject_multiplier_context(cls, data): multiplier = data.get('multiplier') if multiplier: # 为每个Item字段注入上下文 for key in ['field_1', 'field_2']: if key in data: data[key] = Item.model_validate(data[key], context={'multiplier': multiplier}) return data # 测试示例 data = { "multiplier": 5, "field_1": {"value": 1}, "field_2": {"value": 2} } container = Container(**data) print(container.field_1.value) # 输出5 print(container.field_2.value) # 输出10
方法3:自定义通用嵌套模型(适配深层嵌套场景)
如果嵌套层级较深,可以定义通用的子模型类,统一处理父模型参数的传递:
from pydantic import BaseModel, validator, root_validator from typing import TypeVar, Generic T = TypeVar('T') class BaseNestedItem(BaseModel, Generic[T]): value: int @validator("value") @classmethod def validate_value(cls, value, **kwargs): multiplier = kwargs.get('context', {}).get('multiplier', 1) if value * multiplier > 30: raise ValueError(f"value * multiplier 不能超过30") return value * multiplier class Container(BaseModel): multiplier: int field_1: BaseNestedItem field_2: BaseNestedItem @root_validator(pre=True) @classmethod def pass_multiplier_context(cls, values): multiplier = values.get('multiplier') if multiplier: for field in ['field_1', 'field_2']: if field in values: values[field] = BaseNestedItem(**values[field], context={'multiplier': multiplier}) return values
说明
以上方法核心逻辑是通过验证上下文将父模型的属性传递给子模型,既避免了在父层编写复杂的嵌套字段遍历逻辑,也不需要在子模型中重复定义父模型的字段,适配深层嵌套的业务场景。
内容的提问来源于stack exchange,提问作者Ed May
相关产品推荐
相关产品推荐

