如何创建Pydantic泛型类型实现长度校验与进制转换?
实现Pydantic泛型校验类型
需求概述
项目中有大量重复的模型校验逻辑(如密码长度、用户名字符校验),希望通过泛型类实现复用,比如定义String[15,32]、SecretBytes[6,100]这类带参数的类型;同时需要处理不同进制的整数,通过Int[16]或Int[10]让Pydantic自动转换值到指定进制。
一、泛型字符串/秘钥字节校验
实现代码
from typing import Any, Callable, TypeVar, Generic from pydantic_core import core_schema from typing_extensions import get_args from pydantic import BaseModel, SecretBytes # 定义长度约束的类型变量 MinLen = TypeVar('MinLen', bound=int) MaxLen = TypeVar('MaxLen', bound=int) class String(str, Generic[MinLen, MaxLen]): @classmethod def __get_pydantic_core_schema__( cls, source: Any, handler: Callable[[Any], core_schema.CoreSchema] ) -> core_schema.CoreSchema: # 获取泛型参数:最小长度、最大长度 args = get_args(source) min_len, max_len = args if len(args) == 2 else (0, float('inf')) # 构建基础字符串校验规则,包含长度限制 str_schema = core_schema.str_schema( min_length=min_len, max_length=max_len, strict=False ) # 校验后转换为String实例 def validate_and_convert(v: str, info) -> String: return String(v) # 组合校验规则:先做长度校验,再转换类型 schema = core_schema.general_after_validator_function( validate_and_convert, str_schema ) # 支持直接传入String实例 instance_schema = core_schema.is_instance_schema(cls) return core_schema.union_schema([instance_schema, schema]) class SecretBytesGeneric(SecretBytes, Generic[MinLen, MaxLen]): @classmethod def __get_pydantic_core_schema__( cls, source: Any, handler: Callable[[Any], core_schema.CoreSchema] ) -> core_schema.CoreSchema: args = get_args(source) min_len, max_len = args if len(args) == 2 else (0, float('inf')) # 构建带长度限制的秘钥字节校验规则 secret_bytes_schema = core_schema.secret_bytes_schema( min_length=min_len, max_length=max_len, strict=False ) def validate_and_convert(v: bytes | str, info) -> SecretBytesGeneric: return SecretBytesGeneric(v) schema = core_schema.general_after_validator_function( validate_and_convert, secret_bytes_schema ) instance_schema = core_schema.is_instance_schema(cls) return core_schema.union_schema([instance_schema, schema]) # 别名简化使用 SecretBytes = SecretBytesGeneric
测试用例
class User(BaseModel): username: String[15, 32] password: SecretBytes[6, 100] # 合法输入 user = User(username="valid_username_12345", password="secure_pass_123") print(user.username) # valid_username_12345 print(user.password) # SecretBytes(b'secure_pass_123') # 非法输入:用户名长度不足 try: User(username="short", password="secure_pass_123") except Exception as e: print(e) # 输出:1 validation error for User # username # String should have at least 15 characters [type=string_too_short, input_value='short', input_type=str]
二、泛型进制整数转换
实现代码
from typing import Any, Callable, TypeVar, Generic from pydantic_core import core_schema from typing_extensions import get_args from pydantic import BaseModel # 定义进制的类型变量 Base = TypeVar('Base', bound=int) class Int(int, Generic[Base]): @classmethod def __get_pydantic_core_schema__( cls, source: Any, handler: Callable[[Any], core_schema.CoreSchema] ) -> core_schema.CoreSchema: # 获取泛型参数:目标进制 args = get_args(source) base = args[0] if args else 10 # 自定义校验与转换逻辑 def validate_int_with_base(v: Any, info) -> Int: if isinstance(v, str): # 按指定进制解析字符串 try: return Int(int(v, base=base)) except ValueError: raise ValueError(f"无法将字符串'{v}'解析为{base}进制整数") elif isinstance(v, int): # 整数直接返回 return Int(v) else: raise TypeError(f"输入类型{type(v)}不支持,应为字符串或整数") # 基础整数校验规则 + 自定义转换 int_schema = core_schema.int_schema(strict=False) schema = core_schema.general_after_validator_function( validate_int_with_base, int_schema ) # 支持直接传入Int实例 instance_schema = core_schema.is_instance_schema(cls) return core_schema.union_schema([instance_schema, schema])
测试用例
class NumberModel(BaseModel): hex_num: Int[16] dec_num: Int[10] oct_num: Int[8] # 合法输入:字符串按指定进制解析,整数直接接受 model = NumberModel(hex_num="1A", dec_num=26, oct_num="32") print(model.hex_num) # 26 (十进制整数) print(model.dec_num) # 26 print(model.oct_num) # 26 # 非法输入:无法解析为指定进制 try: NumberModel(hex_num="G", dec_num=26, oct_num="32") except Exception as e: print(e) # 输出:1 validation error for NumberModel # hex_num # 无法将字符串'G'解析为16进制整数 [type=value_error, input_value='G', input_type=str]
关键实现要点
- 利用
Generic和TypeVar定义泛型参数,约束参数类型(如长度必须为整数,进制必须合法) - 通过
get_args(source)提取泛型参数(如String[15,32]的参数为(15,32)) - 基于Pydantic核心Schema构建校验规则,结合
general_after_validator_function完成类型转换 - 添加
is_instance_schema支持直接传入自定义类型的实例,提升灵活性
内容的提问来源于stack exchange,提问作者Alan Latte
相关产品推荐
相关产品推荐

