You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归Pydantic模型验证失败时未触发错误问题求助

递归Pydantic模型验证不生效的问题解决

问题现象

为递归结构的Pydantic数据模型添加验证逻辑后,即使子节点的字段不符合验证规则,验证器也未抛出错误,导致非法数据被成功实例化。

代码示例

from __future__ import annotations
from typing import Optional, Union, List
import orjson
from pydantic import BaseModel, validator

def orjson_dumps(v, *, default):
    return orjson.dumps(v, default=default).decode()

class Condition(BaseModel):
    operation: str
    value: Union[List[Condition], Condition, float, int, list, str]
    attribute_name: Optional[str]

    @validator("operation", pre=True, always=True)
    @classmethod
    def validate_operations(cls, field_value):
        if field_value not in ["and", "or", "equal"]:
            msg = f"Supported operations: [and, or, equal]. Passed operation: {field_value}"
            raise ValueError(msg)
        return field_value

if __name__ == '__main__':
    condition4 = {'operation': 'or', 'value': [
        {'operation': 'equal', 'attribute_name': 'num_pay_txns_month', 'value': 6},
        {'operation': 'incorrect_operation', 'attribute_name': 'num_pay_txns_week', 'value': 3}]}
    print(Condition(**condition4))

实际输出

operation='or' value=[{'operation': 'equal', 'attribute_name': 'num_pay_txns_month', 'value': 6}, {'operation': 'incorrect_operation', 'attribute_name': 'num_pay_txns_week', 'value': 3}] attribute_name=None

预期输出

ValueError: Supported operations: [and, or, equal]. Passed operation: incorrect_operation

问题原因

Pydantic在解析Union类型时,会按照从左到右的顺序匹配类型。原代码中list类型位于List[Condition]之前,当value字段传入列表形式的字典时,Pydantic会优先将其识别为普通list类型,而非List[Condition],导致嵌套的字典不会被转换成Condition实例,自然无法触发operation字段的验证器。

解决方案

方案1:调整Union类型顺序

将List[Condition]和Condition放在普通类型(如list)之前,让Pydantic优先尝试解析为模型实例:

class Condition(BaseModel):
    operation: str
    # 把List[Condition]、Condition移到list前面
    value: Union[List[Condition], Condition, float, int, str, list]
    attribute_name: Optional[str]

    # 验证器代码不变
    @validator("operation", pre=True, always=True)
    @classmethod
    def validate_operations(cls, field_value):
        if field_value not in ["and", "or", "equal"]:
            msg = f"Supported operations: [and, or, equal]. Passed operation: {field_value}"
            raise ValueError(msg)
        return field_value

方案2:启用smart_union配置

在模型的Config类中开启smart_union=True,让Pydantic忽略Union类型的顺序,自动选择最匹配的类型进行解析:

class Condition(BaseModel):
    operation: str
    value: Union[List[Condition], Condition, float, int, list, str]
    attribute_name: Optional[str]

    @validator("operation", pre=True, always=True)
    @classmethod
    def validate_operations(cls, field_value):
        if field_value not in ["and", "or", "equal"]:
            msg = f"Supported operations: [and, or, equal]. Passed operation: {field_value}"
            raise ValueError(msg)
        return field_value

    class Config:
        smart_union = True

方案3:自定义递归验证器

给value字段添加自定义验证器,手动递归验证嵌套结构:

class Condition(BaseModel):
    operation: str
    value: Union[List[Condition], Condition, float, int, list, str]
    attribute_name: Optional[str]

    @validator("operation", pre=True, always=True)
    @classmethod
    def validate_operations(cls, field_value):
        if field_value not in ["and", "or", "equal"]:
            msg = f"Supported operations: [and, or, equal]. Passed operation: {field_value}"
            raise ValueError(msg)
        return field_value

    @validator("value")
    @classmethod
    def validate_nested_conditions(cls, field_value):
        if isinstance(field_value, list):
            for item in field_value:
                if isinstance(item, dict):
                    # 手动实例化Condition触发验证
                    Condition(**item)
        elif isinstance(field_value, dict):
            Condition(**field_value)
        return field_value

验证效果

修改后运行测试代码,会触发预期的ValueError:

ValueError: Supported operations: [and, or, equal]. Passed operation: incorrect_operation

内容的提问来源于stack exchange,提问作者Kunal Kishore Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:50:27