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

基于Pydantic实现按字段类型自动应用通用验证器的继承方案问询

在Pydantic BaseModel中按字段类型自动应用通用验证器

问题核心

需要在父类BaseModel中定义通用验证器,让子类继承后自动根据字段类型应用对应验证逻辑:int类型字段用to_int转换,bytes类型字段用to_bytes转换。当前使用通配符*的验证器会对所有字段生效(导致类型不匹配的字段报错),重写model_validate又会丢失Pydantic自带的别名解析功能。

解决方案

利用Pydantic的ValidationInfo参数获取当前字段的类型注解,在验证器内部判断是否执行对应转换,既保留别名解析逻辑,又实现按类型自动应用验证器。

修改后的完整代码

from typing import Union, Optional, Any, get_origin, get_args
from pydantic import BaseModel, ConfigDict, AliasGenerator, AliasChoices, Field, field_validator, ValidationInfo


def to_int(v: Union[str, int]) -> int:
    if isinstance(v, str):
        if v.startswith("0x"):
            return int(v, 16)
        return int(v)
    return v


def to_bytes(v: Union[str, bytes, list[int]]) -> bytes:
    if isinstance(v, bytes):
        return v
    elif isinstance(v, str):
        if v.startswith("0x"):
            return bytes.fromhex(v[2:])
        return v.encode()
    else:
        return bytes(v)


class BaseModelCamelCase(BaseModel):
    model_config = ConfigDict(
        populate_by_name=True,
        alias_generator=AliasGenerator(
            validation_alias=lambda name: AliasChoices(name, name)  # 保留原别名生成逻辑,实际可替换为to_camel
        ),
    )

    @field_validator("*", mode="before")
    def validate_int_fields(cls, v: Any, info: ValidationInfo) -> Any:
        field_type = info.field_annotation
        # 处理Optional等泛型类型
        origin = get_origin(field_type)
        if origin is Union:
            args = get_args(field_type)
            if int in args:
                field_type = int
        
        if field_type is int:
            return to_int(v)
        return v

    @field_validator("*", mode="before")
    def validate_bytes_fields(cls, v: Any, info: ValidationInfo) -> Any:
        field_type = info.field_annotation
        # 处理Optional等泛型类型
        origin = get_origin(field_type)
        if origin is Union:
            args = get_args(field_type)
            if bytes in args:
                field_type = bytes
        
        if field_type is bytes:
            return to_bytes(v)
        return v


class BaseTransactionModel(BaseModelCamelCase):
    nonce: int
    gas: int = Field(validation_alias=AliasChoices("gasLimit", "gas_limit", "gas"))
    to: Optional[bytes]
    value: int
    data: bytes
    r: int = 0
    s: int = 0

关键说明

  1. 类型判断逻辑:通过ValidationInfo.field_annotation获取当前字段的类型,同时处理Optional[int]这类泛型类型(用get_origin和get_args解析泛型参数)。
  2. 验证器触发规则:两个验证器都用通配符*,但内部只对匹配类型的字段执行转换,其他字段直接返回原值,避免类型不匹配报错。
  3. 保留别名解析:没有重写model_validate,完全沿用Pydantic原生的字段填充和别名解析逻辑,包括自定义的validation_alias配置。

测试示例

# 测试数据,包含别名和不同格式的字段值
test_data = {
    "nonce": "0xa",
    "gasLimit": "0x100",
    "to": "0x1234567890abcdef1234567890abcdef12345678",
    "value": "100",
    "data": "0xdeadbeef"
}

model = BaseTransactionModel(**test_data)
print(model.nonce)  # 输出: 10(十六进制字符串转int)
print(model.gas)    # 输出: 256(别名gasLimit被解析,十六进制转int)
print(model.to)     # 输出: b'\x124Vx\x90\xab\xcd\xef\x124Vx\x90\xab\xcd\xef\x124Vx'(十六进制字符串转bytes)
print(model.data)   # 输出: b'\xde\xad\xbe\xef'(十六进制字符串转bytes)

内容的提问来源于stack exchange,提问作者ClementWalter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:03:16