代码之家  ›  专栏  ›  技术社区  ›  Alan Latte

如何创建pydantic泛型类型来检查validate上的min_length和max_length?

  •  0
  • Alan Latte  · 技术社区  · 3 年前

    我有个主意。在我的个人项目中,我有很多方法来检查所描述的模型(检查密码长度、用户名字符等),但如果我描述一个Generic类,它将按照在其中指定某些参数的原则工作呢?

    如:

    
    class User(BaseModel):
         username: String[15,32]
         password: SecretBytes[6,100]
    

    另一个用例: 我经常使用不同的int基,而不是处理值,我希望能够指定类型 Int[16] Int[10] ,如果可能的话,由于pydantic本身将把值转换为我需要的值

    你有什么想法可以做到这一点吗?

    我在官方文档中找到了一个例子,但我不明白如何将其升级到我的愿望。

    from typing import Any, Callable, Sequence, TypeVar
    
    from pydantic_core import ValidationError, core_schema
    from typing_extensions import get_args
    
    from pydantic import BaseModel
    
    T = TypeVar('T')
    
    
    class MySequence(Sequence[T]):
        def __init__(self, v: Sequence[T]):
            self.v = v
    
        def __getitem__(self, i):
            return self.v[i]
    
        def __len__(self):
            return len(self.v)
    
        @classmethod
        def __get_pydantic_core_schema__(
            cls, source: Any, handler: Callable[[Any], core_schema.CoreSchema]
        ) -> core_schema.CoreSchema:
            instance_schema = core_schema.is_instance_schema(cls)
    
            args = get_args(source)
            if args:
                # replace the type and rely on Pydantic to generate the right schema
                # for `Sequence`
                sequence_t_schema = handler.generate_schema(Sequence[args[0]])
            else:
                sequence_t_schema = handler.generate_schema(Sequence)
    
            non_instance_schema = core_schema.general_after_validator_function(
                lambda v, i: MySequence(v), sequence_t_schema
            )
            return core_schema.union_schema([instance_schema, non_instance_schema])
    
    
    class M(BaseModel):
        model_config = dict(validate_default=True)
    
        s1: MySequence = [3]
    
    
    m = M()
    print(m)
    #> s1=<__main__.MySequence object at 0x0123456789ab>
    print(m.s1.v)
    #> [3]
    
    
    class M(BaseModel):
        s1: MySequence[int]
    
    
    M(s1=[1])
    try:
        M(s1=['a'])
    except ValidationError as exc:
        print(exc)
        """
        2 validation errors for M
        s1.is-instance[MySequence]
          Input should be an instance of MySequence [type=is_instance_of, input_value=['a'], input_type=list]
        s1.function-after[<lambda>(), json-or-python[json=list[int],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]].0
          Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
        """
    

    代码来源: https://docs.pydantic.dev/latest/usage/types/custom/#generic-containers

    0 回复  |  直到 3 年前
        1
  •  0
  •   Svidrig_cth01    2 年前

    您可以在python中使用带有雷电方法的Metaclass __getitem__

    我使用python3.9和pydantic1.10,但它需要在pydantic2上工作

    from typing import Annotated, TypeVar
    
    from pydantic import BaseModel, Field
    
    T = TypeVar("T", bound=int)
    
    
    class MetaMaxStr(type):
        def __getitem__(cls, tuple_length: tuple = (0, 1)):
            min_length, max_length = tuple_length
            if min_length > max_length:
                raise ValueError("min_length must be less than max_length")
            return Annotated[str, Field(min_length=min_length, max_length=max_length)]
    
    
    class MaxStr(metaclass=MetaMaxStr):
        pass
    
    
    class Item(BaseModel):
        id: int
        name: MaxStr[5, 5]
    
    
    def main() -> None:
        item = Item(id=1, name="test1")
        print(item.dict())
    
    
    if __name__ == "__main__":
        main()