我有多个 pydantic 2.x 模型,而不是对每个模型上的每个文字字段应用验证
class MyModel(BaseModel):
name: str = ""
description: Optional[str] = None
sex: Literal["male", "female"]
@field_validator("sex", mode="before")
@classmethod
def strip_sex(cls, v: Any, info: ValidationInfo):
if isinstance(v, str):
return v.strip()
return v
我想使用与此类似的方法带注释的验证器
如何实现所有
Literal
字段的自动验证?
def strip_literals(v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v
# doesn't work
# LiteralType = TypeVar("LiteralType", bound=Literal)
# LiteralStripped = Annotated[Literal, BeforeValidator(strip_literals)]
class MyModel(BaseModel):
name: str = ""
description: Optional[str] = None
sex: LiteralStripped["male", "female"]
我想要类似上面的东西,但实际上无法在文字上定义正确的验证处理程序。
您必须将文字值的声明移动到注释中,如下所示:
from typing import Any, Optional, Annotated, TypeVar, Literal
from pydantic import BaseModel, BeforeValidator
def strip_literals(v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v
LiteralStripped = Annotated[Literal["male", "female"], BeforeValidator(strip_literals)]
class MyModel(BaseModel):
name: str = ""
description: Optional[str] = None
sex: LiteralStripped
m = MyModel(sex="foo")
哪个加注:
ValidationError Traceback (most recent call last)
Cell In[38], line 17
14 description: Optional[str] = None
15 sex: LiteralStripped
---> 17 m = MyModel(sex="foo")
File ~/software/mambaforge/envs/tysen-dev/lib/python3.11/site-packages/pydantic/main.py:164, in BaseModel.__init__(__pydantic_self__, **data)
162 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
163 __tracebackhide__ = True
--> 164 __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
ValidationError: 1 validation error for MyModel
sex
Input should be 'male' or 'female' [type=literal_error, input_value='foo', input_type=str]
For further information visit https://errors.pydantic.dev/2.5/v/literal_error
如果您想避免重复注释,您可以根据
__class_getiem__
定义一个别名:
from typing import Any, Optional, Annotated, TypeVar, Literal
from pydantic import BaseModel, BeforeValidator
class LiteralStripped:
@staticmethod
def strip_literals(v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v
def __class_getitem__(cls, values):
return Annotated[Literal[values], BeforeValidator(cls.strip_literals)]
class MyModel(BaseModel):
name: str = ""
description: Optional[str] = None
sex: LiteralStripped["male", "female"]
m = MyModel(sex="foo")
然而,这已经是一种高级模式,但它的工作原理与您的建议完全一样。
我希望这有帮助。