Skip to content

严格模式

API Documentation

pydantic.types.Strict

默认情况下,Pydantic 会在可能时尝试将值强制转换为所需类型。 例如,您可以将字符串 "123" 作为 int 字段的输入,它将被转换为 123。 这种强制行为在许多场景中非常有用——例如:UUID、URL 参数、HTTP 头部、环境变量、 用户输入等。

然而,在某些情况下这是不可取的,您希望 Pydantic 报错而不是强制转换数据。

为了更好地支持这种用例,Pydantic 提供了可以在每个模型、每个字段甚至每次验证调用中启用的"严格模式"。 启用严格模式后,Pydantic 在强制转换数据时将不再那么宽松, 而是会在数据类型不正确时报错。

以下是一个简单示例,展示了严格模式和默认/"宽松"模式下的验证行为差异:

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
    x: int


print(MyModel.model_validate({'x': '123'}))  # 宽松模式
#> x=123

try:
    MyModel.model_validate({'x': '123'}, strict=True)  # 严格模式
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyModel
    x
      Input should be a valid integer [type=int_type, input_value='123', input_type=str]
    """

在使用 Pydantic 时,有多种方法可以实现严格模式验证,下面将详细讨论:

严格模式中的类型强制转换

对于大多数类型,在严格模式下验证来自 Python 的数据时,只接受确切类型的实例。 例如,在验证 int 字段时,只接受 int 的实例;传递 floatstr 的实例 将导致引发 ValidationError

请注意,在严格模式下验证来自 JSON 的数据时,我们会更宽松一些。例如,在验证 UUID 字段时, 从 JSON 验证时会接受 str 实例,但从 Python 验证时不会:

import json
from uuid import UUID

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
    guid: UUID


data = {'guid': '12345678-1234-1234-1234-123456789012'}

print(MyModel.model_validate(data))  # 正常:宽松模式
#> guid=UUID('12345678-1234-1234-1234-123456789012')

print(
    MyModel.model_validate_json(json.dumps(data), strict=True)
)  # 正常:严格模式,但从 json 验证
#> guid=UUID('12345678-1234-1234-1234-123456789012')

try:
    MyModel.model_validate(data, strict=True)  # 不正常:严格模式,从 python 验证
except ValidationError as exc:
    print(exc.errors(include_url=False))
    """
    [
        {
            'type': 'is_instance_of',
            'loc': ('guid',),
            'msg': 'Input should be an instance of UUID',
            'input': '12345678-1234-1234-1234-123456789012',
            'ctx': {'class': 'UUID'},
        }
    ]
    """

有关严格模式下允许哪些类型作为输入的更多详细信息,您可以查看 转换表

方法调用中的严格模式

到目前为止包含的所有示例都通过使用 strict=True 作为验证方法的关键字参数来实现严格模式验证。 虽然我们在 BaseModel.model_validate 中展示了这一点,但这也可以通过 TypeAdapter 用于任意类型:

from pydantic import TypeAdapter, ValidationError

print(TypeAdapter(bool).validate_python('yes'))  # 正常:宽松模式
#> True

try:
    TypeAdapter(bool).validate_python('yes', strict=True)  # 不正常:严格模式
except ValidationError as exc:
    print(exc)
    """
    1 validation error for bool
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

请注意,即使在 TypeAdapter 中使用更"复杂"的类型时,这也有效:

from dataclasses import dataclass

from pydantic import TypeAdapter, ValidationError


@dataclass
class MyDataclass:
    x: int


try:
    TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyDataclass
      Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
    """

这也适用于 TypeAdapter.validate_jsonBaseModel.model_validate_json 方法:

import json
from uuid import UUID

from pydantic import BaseModel, TypeAdapter, ValidationError

try:
    TypeAdapter(list[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
    print(exc)
    """
    2 validation errors for list[int]
    0
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    2
      Input should be a valid integer [type=int_type, input_value='3', input_type=str]
    """


class Model(BaseModel):
    x: int
    y: UUID


data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
    Model.model_validate(data, strict=True)
except ValidationError as exc:
    # 在严格模式下从 python 验证时,x 和 y 都无效:
    print(exc)
    """
    2 validation errors for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    y
      Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
    """

json_data = json.dumps(data)
try:
    Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
    # 从 JSON 验证时,x 在严格模式下仍然无效,但 y 有效:
    print(exc)
    """
    1 validation error for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

使用 Field 的严格模式

对于模型上的各个字段,您可以在字段上设置 strict=True。 这将导致即使在不带 strict=True 调用验证方法时,也会对该字段使用严格模式验证。

只有设置了 strict=True 的字段会受到影响:

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
    name: str
    age: int
    n_pets: int


user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1


class AnotherUser(BaseModel):
    name: str
    age: int = Field(strict=True)
    n_pets: int


try:
    anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
    print(e)
    """
    1 validation error for AnotherUser
    age
      Input should be a valid integer [type=int_type, input_value='42', input_type=str]
    """

请注意,使字段严格也会影响实例化模型类时执行的验证:

from pydantic import BaseModel, Field, ValidationError


class Model(BaseModel):
    x: int = Field(strict=True)
    y: int = Field(strict=False)


try:
    Model(x='1', y='2')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

使用 Field 作为注解

请注意,如果需要,Field(strict=True)(或任何其他关键字参数)可以用作注解,例如, 在使用 TypedDict 时:

from typing import Annotated

from typing_extensions import TypedDict

from pydantic import Field, TypeAdapter, ValidationError


class MyDict(TypedDict):
    x: Annotated[int, Field(strict=True)]


try:
    TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyDict
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """
from typing import Annotated

from typing import TypedDict

from pydantic import Field, TypeAdapter, ValidationError


class MyDict(TypedDict):
    x: Annotated[int, Field(strict=True)]


try:
    TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyDict
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

使用 Annotated[..., Strict()] 的严格模式

API Documentation

pydantic.types.Strict

Pydantic 还提供了 Strict 类,旨在用作 typing.Annotated 类的元数据;此注解表示带注解的字段应在 严格模式下验证:

from typing import Annotated

from pydantic import BaseModel, Strict, ValidationError


class User(BaseModel):
    name: str
    age: int
    is_active: Annotated[bool, Strict()]


User(name='David', age=33, is_active=True)
try:
    User(name='David', age=33, is_active='True')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for User
    is_active
      Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
    """

实际上,这是 Pydantic 提供的某些开箱即用的严格类型(如 StrictInt)的实现方法。

使用 ConfigDict 的严格模式

BaseModel

如果您想为复杂输入类型的所有字段启用严格模式,可以在 model_config 中使用 ConfigDict(strict=True)

from pydantic import BaseModel, ConfigDict, ValidationError


class User(BaseModel):
    model_config = ConfigDict(strict=True)

    name: str
    age: int
    is_active: bool


try:
    User(name='David', age='33', is_active='yes')
except ValidationError as exc:
    print(exc)
    """
    2 validation errors for User
    age
      Input should be a valid integer [type=int_type, input_value='33', input_type=str]
    is_active
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

Note

当通过模型的 model_config 使用 strict=True 时,您仍然可以通过在单个字段上设置 strict=False 来覆盖各个字段的严格性:

from pydantic import BaseModel, ConfigDict, Field


class User(BaseModel):
    model_config = ConfigDict(strict=True)

    name: str
    age: int = Field(strict=False)

请注意,严格模式不会递归应用于嵌套模型字段:

from pydantic import BaseModel, ConfigDict, ValidationError


class Inner(BaseModel):
    y: int


class Outer(BaseModel):
    model_config = ConfigDict(strict=True)

    x: int
    inner: Inner


print(Outer(x=1, inner=Inner(y='2')))
#> x=1 inner=Inner(y=2)

try:
    Outer(x='1', inner=Inner(y='2'))
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

(对于 dataclasses 和 TypedDict 也是如此。)

如果这是不可取的,您应确保为所有涉及的类型启用严格模式。 例如,对于模型类,可以通过使用具有 model_config = ConfigDict(strict=True) 的共享基类来实现:

from pydantic import BaseModel, ConfigDict, ValidationError


class MyBaseModel(BaseModel):
    model_config = ConfigDict(strict=True)


class Inner(MyBaseModel):
    y: int


class Outer(MyBaseModel):
    x: 1
    inner: Inner


try:
    Outer.model_validate({'x': 1, 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    inner.y
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Dataclasses 和 TypedDict

Pydantic dataclasses 的行为与上面使用 BaseModel 所示的示例类似,只是不应使用 model_config, 而应使用 @pydantic.dataclasses.dataclass 装饰器的 config 关键字参数。

在可能的情况下,您可以通过使用 pydantic.types.Strict 注解 注解字段 来为普通 dataclasses 或 TypedDict 子类实现嵌套严格模式。

但是,如果这不可能(例如,在使用第三方类型时),您可以通过在类型上设置 __pydantic_config__ 属性 来设置 Pydantic 应用于该类型的配置:

from typing_extensions import TypedDict

from pydantic import ConfigDict, TypeAdapter, ValidationError


class Inner(TypedDict):
    y: int


Inner.__pydantic_config__ = ConfigDict(strict=True)


class Outer(TypedDict):
    x: int
    inner: Inner


adapter = TypeAdapter(Outer)
print(adapter.validate_python({'x': '1', 'inner': {'y': 2}}))
#> {'x': 1, 'inner': {'y': 2}}


try:
    adapter.validate_python({'x': '1', 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    inner.y
      Input should be a valid integer [type=int_type, input_value='2', input_type=str]
    """
from typing import TypedDict

from pydantic import ConfigDict, TypeAdapter, ValidationError


class Inner(TypedDict):
    y: int


Inner.__pydantic_config__ = ConfigDict(strict=True)


class Outer(TypedDict):
    x: int
    inner: Inner


adapter = TypeAdapter(Outer)
print(adapter.validate_python({'x': '1', 'inner': {'y': 2}}))
#> {'x': 1, 'inner': {'y': 2}}


try:
    adapter.validate_python({'x': '1', 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    inner.y
      Input should be a valid integer [type=int_type, input_value='2', input_type=str]
    """

TypeAdapter

您还可以通过使用 TypeAdapter 类的 config 关键字参数来获得严格模式:

from pydantic import ConfigDict, TypeAdapter, ValidationError

adapter = TypeAdapter(bool, config=ConfigDict(strict=True))

try:
    adapter.validate_python('yes')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for bool
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

@validate_call

严格模式也可与 @validate_call 装饰器一起使用,通过传递 config 关键字参数:

from pydantic import ConfigDict, ValidationError, validate_call


@validate_call(config=ConfigDict(strict=True))
def foo(x: int) -> int:
    return x


try:
    foo('1')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for foo
    0
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """