pydantic.Field搭配自定义数据类型使用时校验不生效问题
问题原因
- Pydantic v1 中,
min_length、max_length这类字符串约束是原生str类型内置校验链的逻辑,不会自动挂载到自定义str子类上。 - 重写
__get_validators__方法后,该类型的校验流程完全由自定义逻辑接管。当前实现仅加入了「输入转字符串」的str_validator和自定义正则校验,既没有内置长度校验逻辑,也没有读取Field对象中传入的长度配置参数,因此配置的长度规则不会触发。
解决方案
以下两种方案都可以实现校验逻辑复用,不需要在每个字段重复编写校验规则:
方案1:在自定义类型中适配Field长度参数
修改Password类实现,在校验链中加入长度校验逻辑,自动读取Field配置的长度参数,同时支持设置默认长度约束:
from re import match from typing import Callable import pydantic from pydantic.validators import str_validator from pydantic.fields import ModelField class Password(str): # 配置默认长度约束,字段无特殊配置时自动生效 default_min_length: int = 8 default_max_length: int = 128 @classmethod def __get_validators__(cls) -> Callable: yield str_validator yield cls.validate_length yield cls.validate_pattern @classmethod def validate_length(cls, value: str, field: ModelField) -> str: # 优先读取Field传入的长度配置,无配置则使用类默认值 min_len = field.field_info.min_length or cls.default_min_length max_len = field.field_info.max_length or cls.default_max_length val_len = len(value) if val_len < min_len: raise ValueError(f"Password length must be at least {min_len} characters") if val_len > max_len: raise ValueError(f"Password length cannot exceed {max_len} characters") return value @classmethod def validate_pattern(cls, value: str) -> str: regexp = ( r"(?=.*?[a-z])" r"(?=.*?[A-Z])" r"(?=.*?[0-9])" ) if not match(regexp, value): raise ValueError( "Password must contain at least one " "lower/upper case letter, one digit." ) return value
该实现完全兼容原有写法,特殊场景下直接在Field中传入min_length/max_length即可覆盖默认长度规则。
方案2:继承Pydantic内置约束字符串类型
直接基于Pydantic自带的constr(约束字符串类型)实现,不需要自行编写长度校验逻辑,constr原生支持所有字符串约束的参数读取、校验和默认值覆盖逻辑:
from re import match from typing import Callable import pydantic from pydantic import constr # 定义带默认长度约束的密码基类 BasePassword = constr(min_length=8, max_length=128) class Password(BasePassword): @classmethod def __get_validators__(cls) -> Callable: # 先加载constr自带的所有校验(类型转换、长度校验等) yield from super().__get_validators__() # 追加自定义密码格式校验 yield cls.validate_pattern @classmethod def validate_pattern(cls, value: str) -> str: regexp = ( r"(?=.*?[a-z])" r"(?=.*?[A-Z])" r"(?=.*?[0-9])" ) if not match(regexp, value): raise ValueError( "Password must contain at least one " "lower/upper case letter, one digit." ) return value
该方案行为和原生字符串类型完全一致,代码更简洁,不需要手动处理字段参数读取逻辑。
内容的提问来源于stack exchange,提问作者Michael Aboryone
相关产品推荐
相关产品推荐

