跳转至

迁移指南

Pydantic V2 对 API 进行了一些更改,包括一些破坏性更改。

此页面提供了一份指南,突出显示了最重要的更改,以帮助您将代码从 Pydantic V1 迁移到 Pydantic V2。

Install Pydantic V2

Pydantic V2 现为 Pydantic 的当前生产发布版本。可从 PyPI 安装 Pydantic V2:

pip install -U pydantic

如果遇到任何问题,请在 GitHub 中使用 bug V2 标签创建一个问题。这将有助于我们积极监控和跟踪错误,并继续改进库的性能。

如果因任何原因需要使用最新的 Pydantic V1,请参阅下面的继续使用 Pydantic V1 功能部分,以获取有关安装和从 pydantic.v1 导入的详细信息。

代码转换工具

我们创建了一个工具来帮助您迁移代码。这个工具仍处于测试阶段,但我们希望它能帮助您更快地迁移代码。

你可以从 PyPI 安装该工具:

pip install bump-pydantic

用法很简单。如果你的项目结构是:

* repo_folder
    * my_package
        * <python source files> ...

Then you'll want to do:

cd /path/to/repo_folder
bump-pydantic my_package

查看关于它在 Bump Pydantic 存储库的更多信息。

继续使用 Pydantic V1 特性

Pydantic V1 仍然可用,当你需要时,不过我们建议迁移到 Pydantic V2,因为它有改进和新功能。

如果需要使用最新的 Pydantic V1,可以用以下方式安装:

pip install "pydantic==1.*"

Pydantic V2 包也通过 pydantic.v1 导入继续提供对 Pydantic V1 API 的访问。

例如,你可以使用 Pydantic V1 的 BaseModel 类而不是 Pydantic V2 的 pydantic.BaseModel 类:

from pydantic.v1 import BaseModel

你也可以导入已从 Pydantic V2 中移除的函数,例如 lenient_isinstance

from pydantic.v1.utils import lenient_isinstance

Pydantic V1 文档可在 https://pydantic.com.cn/1.10/ 获得。

在 v1/v2 环境中使用 Pydantic v1 特性

截至 pydantic>=1.10.17pydantic.v1 命名空间可在 V1 内使用。这使得向也支持 pydantic.v1 命名空间的 V2 迁移更容易。为了解除对 pydantic<2 依赖项的固定并继续使用 V1 功能,采取以下步骤:

  1. 替换 pydantic<2pydantic>=1.10.17
  2. 查找并替换所有出现的:

    from pydantic. import

    with:

    from pydantic.v1.<module> import <object>
    

    这里是根据你所使用的 pydantic 的版本导入 pydantic 的 v1 功能的方法:

    ”截至 v1.10.17.v1 命名空间在 V1 中可用,可如下导入:

    from pydantic.v1.fields import ModelField
    

    ” 所有 Pydantic V1 和 V2 版本都支持以下导入模式,以防你不知道使用的是 Pydantic 的哪个版本:

    try:
        from pydantic.v1.fields import ModelField
    except ImportError:
        from pydantic.fields import ModelField
    

    注意

    当使用 pydantic>=1.10.17,<2 带有 .v1 命名空间导入模块时,这些模块与没有 .v1 命名空间的相同导入的模块将不是相同的模块,但导入的符号将是。例如 pydantic.v1.fields is not pydantic.fieldspydantic.v1.fields.ModelField is pydantic.fields.ModelField 。幸运的是,在绝大多数情况下这不太可能相关。这只是提供更顺畅的迁移体验的一个不幸后果。

    迁移指南

    以下各节提供了关于 Pydantic V2 中最重要更改的详细信息。

    pydantic.BaseModel 的更改

    各种方法名称已更改;所有非弃用的 BaseModel 方法现在的名称都符合 model_.*__.*pydantic.*__ 的格式。在可能的情况下,我们保留了具有旧名称的已弃用方法以帮助简化迁移,但调用它们会发出 DeprecationWarning 警告。

    Pydantic V1 Pydantic V2
    __fields__ model_fields
    __private_attributes__ __pydantic_private__
    __validators__ __pydantic_validator__
    construct() model_construct()
    copy() model_copy()
    dict() model_dump()
    json_schema() model_json_schema()
    json() model_dump_json()
    parse_obj() model_validate()
    update_forward_refs() model_rebuild()
    • 一些内置的数据加载功能已被列入移除计划。特别是 parse_rawparse_file 现在已被弃用。在 Pydantic V2 中, model_validate_json 的工作方式类似于 parse_raw 。否则,你应该加载数据,然后将其传递给 model_validate

    • from_orm 方法已弃用;现在只需使用 model_validate (等同于 Pydantic V1 中的 parse_obj )即可实现类似的功能,只要在模型配置中设置了 from_attributes=True

    • __eq__ 方法已针对模型发生改变。

      • 模型只能等于其他 BaseModel 实例。

      • 对于两个模型实例要相等,它们必须具有相同的:

        • 类型(或者对于通用模型来说,无参数化的通用原始类型)

        • Field values
        • 额外值(仅在 model_config['extra'] == 'allow' 相关时)

        • 私有属性值;具有不同私有属性值的模型不再相等。

        • 模型不再等同于包含其数据的字典。

        • 非通用的不同类型模型从来都不相等。

        • 通用模型具有不同的起源类型永远不相等。我们不需要确切的类型相等,以便例如 MyGenericModel[Any] 的实例可以等于 MyGenericModel[int] 的实例。

    • 我们已用一种名为 RootModel 的新类型替换了使用 __root__ 字段来指定“自定义根模型”的方式,该类型旨在替换在 Pydantic V1 中使用名为 __root__ 的字段的功能。注意, RootModel 类型不再支持 arbitrary_types_allowed 配置设置。参见此问题评论以获取解释。

    • 我们显著扩展了 Pydantic 与自定义序列化相关的功能。特别是,我们添加了 @field_serializer@model_serializer@computed_field 装饰器,它们各自解决了 Pydantic V1 的各种不足之处。

      • 查看这些新装饰器的用法文档的自定义序列化器。

      • 由于性能开销和实现复杂性,我们现在已弃用在模型配置中指定 json_encoders 的支持。此功能最初是为了实现自定义序列化逻辑而添加的,我们认为在大多数常见场景中,新的序列化修饰符是更好的选择。

    • 我们已更改了在父模型中作为嵌套字段出现的模型子类序列化相关的行为。在 V1 中,我们总是会包含子类实例的所有字段。在 V2 中,当转储模型时,我们仅包含在字段的已注释类型上定义的字段。这有助于防止一些意外的安全漏洞。你可以在模型导出文档的“基模型字段的子类实例、数据类、类型定义字典”部分了解更多相关内容(包括如何选择退出此行为)。

    • GetterDict 已被移除,因为它只是 orm_mode 的一个实现细节,而 orm_mode 已被移除。

    • 在许多情况下,传递给构造函数的参数将被复制以进行验证,并且在必要时进行强制转换。在将可变对象作为参数传递给构造函数的情况下这一点很明显。你可以在这里看到一个示例+更多详细信息。

    • .json() 方法已弃用,若使用此已弃用的方法并带有 indentensure_ascii 等参数,可能会导致令人困惑的错误。为获得最佳效果,请切换到 V2 的等效方法 model_dump_json() 。如果仍想使用上述参数,可以使用此解决方法。

    • JSON 对非字符串键值的序列化通常使用 str(key) ,会导致以下一些行为变化:

    from typing import Dict, Optional
    
    from pydantic import BaseModel as V2BaseModel
    from pydantic.v1 import BaseModel as V1BaseModel
    
    
    class V1Model(V1BaseModel):
        a: Dict[Optional[str], int]
    
    
    class V2Model(V2BaseModel):
        a: Dict[Optional[str], int]
    
    
    v1_model = V1Model(a={None: 123})
    v2_model = V2Model(a={None: 123})
    
    # V1
    print(v1_model.json())
    #> {"a": {"null": 123}}
    
    # V2
    print(v2_model.model_dump_json())
    #> {"a":{"None":123}}
    
    • model_dump_json() 的结果被压缩以节省空间,并不总是完全与 json.dumps() 输出匹配。话虽如此,你可以轻松修改 json.dumps() 结果中使用的分隔符,以使两个输出对齐

    import json
    from typing import List
    
    from pydantic import BaseModel as V2BaseModel
    from pydantic.v1 import BaseModel as V1BaseModel
    
    
    class V1Model(V1BaseModel):
        a: List[str]
    
    
    class V2Model(V2BaseModel):
        a: List[str]
    
    
    v1_model = V1Model(a=['fancy', 'sushi'])
    v2_model = V2Model(a=['fancy', 'sushi'])
    
    # V1
    print(v1_model.json())
    #> {"a": ["fancy", "sushi"]}
    
    # V2
    print(v2_model.model_dump_json())
    #> {"a":["fancy","sushi"]}
    
    # Plain json.dumps
    print(json.dumps(v2_model.model_dump()))
    #> {"a": ["fancy", "sushi"]}
    
    # Modified json.dumps
    print(json.dumps(v2_model.model_dump(), separators=(',', ':')))
    #> {"a":["fancy","sushi"]}
    

    pydantic.generics.GenericModel 的更改

    pydantic.generics.GenericModel 类不再必要,已被移除。现在,只需将 Generic 作为父类直接添加到 BaseModel 子类上即可创建通用的 BaseModel 子类。这看起来像 class MyGenericModel(BaseModel, Generic[T]): ...

    V1 和 V2 模型的混合不被支持,这意味着这样的泛型 BaseModel (V2)的类型参数不能是 V1 模型。

    虽然它可能不会引发错误,但我们强烈建议不要在 isinstance 检查中使用参数化泛型。

    • 例如,不应该做 isinstance(my_model, MyGenericModel[int]) 。然而,做 isinstance(my_model, MyGenericModel) 是可以的。(注意,对于标准泛型,用参数化泛型进行子类检查会引发错误。)

    • 如果需要针对参数化泛型执行 isinstance 检查,可以通过继承参数化泛型类来实现。这看起来像 class MyIntModel(MyGenericModel[int]): ...isinstance(my_model, MyIntModel)

    在通用模型文档中查找更多信息。

    pydantic.Field 的更改

    Field 不再支持向 JSON 模式中添加任意关键字参数。相反,您想要添加到 JSON 模式中的任何额外数据应作为字典传递给 json_schema_extra 关键字参数。

    在 Pydantic V1 中, alias 属性在未设置别名时返回字段的名称。在 Pydantic V2 中,这种行为已改为在未设置别名时返回 None

    以下属性已从 Field 中移除或更改:

    • const
    • min_items (用 min_length 代替)
    • max_items (用 max_length 代替)
    • unique_items
    • allow_mutation (用 frozen 代替)
    • regex (用 pattern 代替)
    • final (使用 [typing.Final] 类型提示)

    字段约束不再自动推到泛型的参数中。例如,不能通过提供 my_list: list[str] = Field(pattern=".*") 来验证列表的每个元素是否与正则表达式匹配。相反,使用 [ typing.Annotated ][] 在 str 自身上提供注释: my_list: list[Annotated[str, Field(pattern=".*")]]

    • [待办:需要记录 pydantic.Field 中其他向后不兼容的更改]

    数据类的更改

    Pydantic 数据类在使标准数据类能够进行数据验证而无需子类化 BaseModel 时仍然很有用。Pydantic V2 对这种数据类行为引入了以下更改:

    • 当用作字段时,数据类(Pydantic 或普通的)不再接受元组作为验证输入;应使用字典。

    • 在 Pydantic 数据类中, __post_init__ 现在将在验证后而不是验证前被调用。

      • 因此, __post_init_post_parse__ 方法已经变得多余,所以已被删除。

    • Pydantic 不再支持 extra='allow' 用于 Pydantic 数据类,通过初始化器传递的额外字段将作为额外属性存储在数据类上。 extra='ignore' 仍被支持用于在解析数据时忽略意外字段的目的,它们只是不会存储在实例上。

    • Pydantic 的数据类不再有 __pydantic_model__ 属性,也不再使用底层的 BaseModel 来进行验证或提供其他功能。

      • 进行验证、生成 JSON 模式或利用 V1 中可能需要 __pydantic_model__ 的任何其他功能时,现在应将数据类用 TypeAdapter (下面将更详细讨论)进行包装,并使用其方法。

    • 在 Pydantic V1 中,如果将一个普通(即非 Pydantic 的)数据类用作字段,那么父类型的配置就会被当作该数据类自身的配置一样使用。在 Pydantic V2 中,情况不再如此。

      • 在 Pydantic V2 中,要覆盖配置(就像在 BaseModel 上使用 model_config 那样),可以在 @dataclass 装饰器上使用 config 参数。有关示例,请参阅数据类配置。

    配置的更改

    • 在 Pydantic V2 中,要在模型上指定配置,应将一个名为 model_config 的类属性设置为一个带有作为配置要使用的键值对的字典。在父 BaseModel 子类的命名空间中创建名为 Config 的类的 Pydantic V1 行为现已弃用。

    • 当子类化一个模型时, `model_config` 属性会被继承。这在以下情况下很有用:您希望为许多模型使用具有给定配置的基类。请注意,如果从多个 `BaseModel` 子类继承,如 `class MyModel(Model1, Model2)` ,则来自这两个模型的 `model_config` 属性中非默认设置将被合并,对于在两者中都定义的任何设置,来自 `Model2` 的将覆盖来自 `Model1` 的。
      
    • 以下配置设置已被移除:
      
      • allow_mutation ——已被移除。你应该能够等价使用冻结(当前使用的反义)。

      • error_msg_templates
      • fields ——这是各种错误的根源,所以已被移除。您应该能够在字段上使用 Annotated 来按所需修改它们。

      • getter_dictorm_mode 已被移除,此实现细节不再必要。

      • smart_union.
      • underscore_attrs_are_private ——Pydantic V2 的行为现在与在 Pydantic V1 中始终将其设置为 True 时的行为相同。

      • json_loads
      • json_dumps
      • copy_on_model_validation
      • post_init_call
      • 以下配置设置已重命名:

      • allow_population_by_field_namepopulate_by_name

      • anystr_lowerstr_to_lower
      • anystr_strip_whitespacestr_strip_whitespace
      • anystr_upperstr_to_upper
      • keep_untouchedignored_types
      • max_anystr_lengthstr_max_length
      • min_anystr_lengthstr_min_length
      • orm_modefrom_attributes
      • schema_extrajson_schema_extra
      • validate_allvalidate_default

    查看 ConfigDict API 参考 以获取更多详细信息。

    变更验证者

    @validator@root_validator 已弃用

    • @validator 已被弃用,应替换为 @field_validator ,它提供了各种新功能和改进。

      • 新的 @field_validator 修饰符没有 each_item 关键字参数;要应用于泛型容器中的项的验证器应通过注释类型参数来添加。有关详细信息,请参阅注释元数据中的验证器。这看起来像 List[Annotated[int, Field(ge=0)]]

      • 即使你继续使用已弃用的 @validator 装饰器,也无法再向验证器函数的签名中添加 fieldconfig 参数。如果需要访问这些,就需要迁移到 @field_validator ——有关更多详细信息,请参阅下一节。

      • 如果在验证器函数中使用 always=True 关键字参数,则请注意,即使是针对注释类型的标准验证器也会应用,不仅仅是自定义验证器。例如,尽管下面的验证器永远不会出错,但以下代码会引发 ValidationError

    注意

    为避免此情况,可在 Field 函数中使用 validate_default 参数。当设置为 True 时,它模拟 Pydantic v1 中的 always=True 行为。但鼓励使用 validate_default 的新方式,因为它提供了更多的灵活性和控制权。

    from pydantic import BaseModel, validator
    
    
    class Model(BaseModel):
        x: str = 1
    
        @validator('x', always=True)
        @classmethod
        def validate_x(cls, v):
            return v
    
    
    Model()
    
    • @root_validator 已被弃用,应替换为 @model_validator ,它还提供了新功能和改进。

      • 在某些情况下(例如在 model_config['validate_assignment'] is True 赋值时), @model_validator 装饰器将接收模型的实例,而不是值的字典。您可能需要小心处理这种情况。

      • 即使您继续使用已弃用的 @root_validator 装饰器,由于验证逻辑的重构,您无法再使用 skip_on_failure=False (这是该关键字参数的默认值,因此必须显式设置为 True )。

    @validator 的允许签名的更改

    在 Pydantic V1 中,由 @validator 包装的函数可以接收带有关于正在验证的内容的元数据的关键字参数。在 Pydantic V2 中,一些这些参数已从 @field_validator 中移除:

    • config :Pydantic V2 的配置现在是一个字典而不是一个类,这意味着这个参数不再向后兼容。如果需要访问配置,应迁移到 @field_validator 并使用 info.config

    • field :该参数曾是一个 ModelField 对象,它是 Pydantic V2 中不再存在的准内部类。大多数此类信息仍可通过使用来自 info.field_name 的字段名索引到 cls.model_fields 来访问

    from pydantic import BaseModel, ValidationInfo, field_validator
    
    
    class Model(BaseModel):
        x: int
    
        @field_validator('x')
        def val_x(cls, v: int, info: ValidationInfo) -> int:
            assert info.config is not None
            print(info.config.get('title'))
            #> Model
            print(cls.model_fields[info.field_name].is_required())
            #> True
            return v
    
    
    Model(x=1)
    

    TypeError 不再在验证器中转换为 ValidationError

    之前,在验证器函数中引发 TypeError 时,该错误会被包装到 ValidationError 中,并且在某些情况下(例如使用 FastAPI 时),这些错误可能会显示给最终用户。这导致了各种不良行为——例如,使用错误的签名调用函数可能会产生面向用户的 ValidationError

    然而,在 Pydantic V2 中,当在验证器中引发 TypeError 时,它不再转换为 ValidationError

    import pytest
    
    from pydantic import BaseModel, field_validator  # or validator
    
    
    class Model(BaseModel):
        x: int
    
        @field_validator('x')
        def val_x(cls, v: int) -> int:
            return str.lower(v)  # raises a TypeError
    
    
    with pytest.raises(TypeError):
        Model(x=1)
    

    这适用于所有验证装饰器。

    验证器行为的改变

    Pydantic V2 包含对类型强制转换的一些更改。例如:

    • 强制 intfloatDecimal 值转换为字符串现在是可选的且默认禁用,参见 将数字强制转换为字符串

    • 可迭代的键值对不再强制转换为字典。

    查看 Pydantic V2 类型强制默认的转换表以获取详细信息。

    allow_reuse 关键字参数不再必要

    先前,Pydantic 会追踪装饰器中的“被重用”函数,因为这是常见的错误来源。我们通过比较函数的完全限定名称(模块名 + 函数名)来做到这一点,这可能会导致误报。当这是有意的时,可以使用 allow_reuse 关键字参数来禁用此功能。

    我们检测重复定义函数的方法已经进行了全面修改,仅在单个类内对重新定义报错,减少了误报,并使行为更符合类型检查器和代码清理器在单个类定义中多次定义同名方法时给出的错误行为。

    在几乎所有情况下,如果您正在使用 allow_reuse=True ,那么您应该能够简单地删除那个关键字参数,并且让事情像预期的那样继续正常工作。

    @validate_arguments 已更名为 @validate_call

    在 Pydantic V2 中, @validate_arguments 修饰符已更名为 @validate_call

    在 Pydantic V1 中,装饰后的函数添加了各种属性,如 raw_functionvalidate (可用于在不实际调用装饰后的函数的情况下验证参数)。由于这些属性的使用有限且在实现方面出于性能导向的更改,我们在 @validate_call 中没有保留此功能。

    输入类型未被保留

    在 Pydantic V1 中,当通用集合的所有字段输入是字段注释的恰当子类型时,我们做出了巨大努力来保留它们的类型。例如,给定注释 Mapping[str, int] ,如果你传入一个 collection.Counter() ,你将得到一个 collection.Counter() 作为值。

    支持 V2 中的这种行为会对一般情况产生负面影响(每次都必须检查类型),并且会给验证增加大量复杂性。此外,即使在 V1 中,这种行为也是不一致且部分损坏的:它不适用于许多类型( strUUID 等),对于泛型集合,如果没有大量特殊情况处理(考虑 ChainMap ;重建输入是必要的,因为我们需要在验证后替换值,例如如果将字符串强制转换为整数),就不可能正确重建原始输入。

    在 Pydantic V2 中,我们不再试图在所有情况下都保留输入类型;相反,我们仅承诺输出类型将与类型注释匹配。 回到 Mapping 这个示例,我们保证输出将是一个有效的 Mapping ,并且在实际中它将是一个普通的 dict

    from typing import Mapping
    
    from pydantic import TypeAdapter
    
    
    class MyDict(dict):
        pass
    
    
    ta = TypeAdapter(Mapping[str, int])
    v = ta.validate_python(MyDict())
    print(type(v))
    #> <class 'dict'>
    

    如果您希望输出类型为特定类型,考虑将其标注为这样或实现自定义验证器:

    from typing import Any, Mapping, TypeVar
    
    from typing_extensions import Annotated
    
    from pydantic import (
        TypeAdapter,
        ValidationInfo,
        ValidatorFunctionWrapHandler,
        WrapValidator,
    )
    
    
    def restore_input_type(
        value: Any, handler: ValidatorFunctionWrapHandler, _info: ValidationInfo
    ) -> Any:
        return type(value)(handler(value))
    
    
    T = TypeVar('T')
    PreserveType = Annotated[T, WrapValidator(restore_input_type)]
    
    
    ta = TypeAdapter(PreserveType[Mapping[str, int]])
    
    
    class MyDict(dict):
        pass
    
    
    v = ta.validate_python(MyDict())
    assert type(v) is MyDict
    

    虽然我们不保证在所有地方都保留输入类型,但我们确实为 BaseModel 的子类以及数据类保留它们:

    import pydantic.dataclasses
    from pydantic import BaseModel
    
    
    class InnerModel(BaseModel):
        x: int
    
    
    class OuterModel(BaseModel):
        inner: InnerModel
    
    
    class SubInnerModel(InnerModel):
        y: int
    
    
    m = OuterModel(inner=SubInnerModel(x=1, y=2))
    print(m)
    #> inner=SubInnerModel(x=1, y=2)
    
    
    @pydantic.dataclasses.dataclass
    class InnerDataclass:
        x: int
    
    
    @pydantic.dataclasses.dataclass
    class SubInnerDataclass(InnerDataclass):
        y: int
    
    
    @pydantic.dataclasses.dataclass
    class OuterDataclass:
        inner: InnerDataclass
    
    
    d = OuterDataclass(inner=SubInnerDataclass(x=1, y=2))
    print(d)
    #> OuterDataclass(inner=SubInnerDataclass(x=1, y=2))
    

    更改标准类型的处理方式

    Dicts

    可包含空可迭代项的键值对可迭代项不再通过对类型 dict 的字段的验证 。

    Unions

    虽然联合类型仍会从左到右尝试对每个选项进行验证,但现在它们尽可能保留输入的类型,即使正确类型不是输入通过验证的第一个选项。作为演示,考虑以下示例:

    from typing import Union
    
    from pydantic import BaseModel
    
    
    class Model(BaseModel):
        x: Union[int, str]
    
    
    print(Model(x='1'))
    #> x='1'
    

    在 Pydantic V1 中,打印结果将是 x=1 ,因为该值将作为 int 通过验证。在 Pydantic V2 中,我们认识到该值是其中一种情况的实例,并短路标准联合验证。

    要恢复 V1 的非短路从左到右行为,在并集上标注 Field(union_mode='left_to_right') 。有关并集模式的更多详细信息请参见。

    必填、可选和可为空的字段

    Pydantic V2 对指定字段标注为 Optional 是否为必填(即没有默认值)还是非必填(即有默认值为 None 或对应类型的其他值)的逻辑进行了一些更改,现在更紧密地匹配了 dataclasses 的行为。同样,标注为 Any 的字段不再具有默认值为 None

    以下表格描述了 V2 中字段注释的行为:

    State Field Definition
    Required, cannot be None f1: str
    Not required, cannot be None, is 'abc' by default f2: str = 'abc'
    Required, can be None f3: Optional[str]
    Not required, can be None, is None by default f4: Optional[str] = None
    Not required, can be None, is 'abc' by default f5: Optional[str] = 'abc'
    Required, can be any type (including None) f6: Any
    Not required, can be any type (including None)

    Note

    将标注为 typing.Optional[T] 的字段作为必填项,且允许值为 None 。这并不意味着该字段具有 None 的默认值。(这与 V1 相比是一个重大更改。)

    Note

    如果提供了任何默认值,则使该字段无需必填。

    这里是一个展示上述内容的代码示例:

    Here is a code example demonstrating the above:

    from typing import Optional
    
    from pydantic import BaseModel, ValidationError
    
    
    class Foo(BaseModel):
        f1: str  # required, cannot be None
        f2: Optional[str]  # required, can be None - same as str | None
        f3: Optional[str] = None  # not required, can be None
        f4: str = 'Foobar'  # not required, but cannot be None
    
    
    try:
        Foo(f1=None, f2=None, f4='b')
    except ValidationError as e:
        print(e)
        """
        1 validation error for Foo
        f1
          Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
        """
    

    模式/字符串上的正则表达式

    Pydantic V1 使用了 Python 的正则表达式库。Pydantic V2 使用了 Rust 的正则表达式 crate。这个 crate 不仅仅是“正则表达式的 Rust 版本”,它是一种完全不同的正则表达式处理方式。特别是,它承诺以牺牲一些功能(即左右环视和反向引用)为代价实现字符串的线性时间搜索。我们认为这是一个值得的权衡,特别是因为 Pydantic 用于验证不可信的输入,在这种情况下确保事情不会根据不可信的输入意外地以指数时间运行是很重要的。另一方面,对于不使用这些功能的人来说,复杂的正则表达式验证应该快几个数量级,因为它是在 Rust 中并以线性时间完成的。

    如果您仍然想要使用 Python 的正则表达式库,可以使用 regex_engine 配置设置。

    TypeAdapter 的介绍

    Pydantic V1 在验证或序列化非 BaseModel 类型方面支持较弱。

    与他们合作,你要么创建一个“根”模型,要么使用 pydantic.tools 中的实用函数(即 parse_obj_asschema_of )。

    在 Pydantic V2 中这要容易得多: TypeAdapter 类允许创建具有用于验证、序列化和为任意类型生成 JSON 模式的方法的对象。这可作为 parse_obj_asschema_of (现在已弃用)的完全替代,并且还涵盖了“根”模型的一些用例。(上面讨论的 RootModel 涵盖了其他用例。)

    from typing import List
    
    from pydantic import TypeAdapter
    
    adapter = TypeAdapter(List[int])
    assert adapter.validate_python(['1', '2', '3']) == [1, 2, 3]
    print(adapter.json_schema())
    #> {'items': {'type': 'integer'}, 'type': 'array'}
    

    由于常见类型检查器在推断泛型类型方面存在局限性,为了在某些场景中获得正确的类型,您可能需要显式指定泛型参数:

    from pydantic import TypeAdapter
    
    adapter = TypeAdapter[str | int](str | int)
    ...
    

    查看类型适配器以获取更多信息。

    Defining custom types

    我们已经完全对在 pydantic 中自定义类型的定义方式进行了彻底改造。

    我们已经公开了用于生成 pydantic-core 和 JSON 模式的钩子,这使您即使在使用自己的自定义类型时也能获得 Pydantic V2 的所有性能优势。

    我们还引入了使用 [ typing.Annotated ] 来为自己的类型添加自定义验证的方法。

    The main changes are:

    • __get_validators__ 应替换为 __get_pydantic_core_schema__ 。有关更多信息,请参阅自定义数据类型。

    • __modify_schema__ 变为 __get_pydantic_json_schema__ 。有关更多信息,请参阅 JSON 模式自定义。

    此外,您可以使用 [ typing.Annotated ][] 通过注释来修改或提供类型的 __get_pydantic_core_schema____get_pydantic_json_schema__ 功能,而不是修改类型本身。这为将第三方类型与 Pydantic 集成提供了强大而灵活的机制,在某些情况下可能有助于您从为解决自定义类型的限制而在 Pydantic V1 中引入的黑客中移除。

    查看自定义数据类型以获取更多信息。

    对 JSON 模式生成的更改

    我们这些年来收到了许多关于对 pydantic 生成的 JSON 模式进行更改的请求。

    在 Pydantic V2 中,我们已经尝试解决了许多常见的请求:

    • Optional 字段的 JSON 模式现在表示允许值 null

    • Decimal 类型现在在 JSON 模式(及序列化后)中作为字符串公开。

    • JSON 模式不再将命名元组保留为命名元组。

    • 我们现在默认生成的 JSON 模式目标是 2020-12 版草案(带有一些 OpenAPI 扩展)。

    • 当它们不同时,现在可以指定您是希望 JSON 模式表示输入的验证,还是表示序列化的输出。

    然而,多年来已经有很多合理的变更请求,但我们没有选择实施。

    在 Pydantic V1 中,即使你愿意自己实现更改,也非常困难,因为 JSON 模式生成过程涉及各种递归函数调用;要覆盖一个,你就得复制并修改整个实现。

    在 Pydantic V2 中,我们的一个设计目标是使其更容易自定义 JSON 模式生成。为此,我们引入了类 GenerateJsonSchema ,它实现了将类型的 pydantic-core 模式转换为 JSON 模式。从设计上讲,这个类将 JSON 模式生成过程分解为较小的方法,这些方法可以在子类中轻松覆盖以修改生成 JSON 模式的“全局”方法。

    各种可用于生成 JSON 模式的方法(如 BaseModel.model_json_schemaTypeAdapter.json_schema )接受关键字参数 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema ,并且可以将自定义子类传递给这些方法,以使用自己的方式生成 JSON 模式。

    希望这意味着如果您不同意我们所做的任何选择,或者如果您依赖于在 Pydantic V2 中已更改的 Pydantic V1 中的行为,您可以使用自定义 schema_generator ,根据您的应用程序需要修改 GenerateJsonSchema 类。

    BaseSettings 已迁移至 pydantic-settings

    BaseSettings ,Pydantic 设置管理的基本对象已移至单独的包 pydantic-settings

    此外, parse_env_var 类方法已被移除。因此,你需要自定义设置源以拥有自己的解析函数。

    颜色和支付卡号移至 pydantic-extra-types

    以下这些特殊用途类型已被移到 Pydantic Extra Types 包中,如果需要,可以单独安装。

    网址和数据源类型在 pydantic.networks 不再继承自 str

    在 Pydantic V1 中, AnyUrl 类型继承自 str ,所有其他 UrlDsn 类型都从这些继承而来。在 Pydantic V2 中,这些类型基于使用 Annotated 的两个新的 UrlMultiHostUrl 类构建。

    继承自 str 有优点也有缺点,对于 V2 我们决定最好移除这个。要在期望 str 的 API 中使用这些类型,现在需要将它们转换(使用 str(url) )。

    Pydantic V2 使用 Rust 的 Url 包进行 URL 验证。一些 URL 验证与 V1 中的先前行为略有不同。一个显著的区别是,如果没有包含路径,新的 Url 类型会在验证后的版本中添加斜杠,即使在 Url 类型构造函数的参数中没有指定斜杠。请看下面的示例了解此行为:

    from pydantic import AnyUrl
    
    assert str(AnyUrl(url='https://google.com')) == 'https://google.com/'
    assert str(AnyUrl(url='https://google.com/')) == 'https://google.com/'
    assert str(AnyUrl(url='https://google.com/api')) == 'https://google.com/api'
    assert str(AnyUrl(url='https://google.com/api/')) == 'https://google.com/api/'
    

    如果你仍然想要使用没有附加斜杠的旧行为,请看一下这个解决方案。

    Constrained types

    Constrained* 类已被移除,应替换为 Annotated[<type>, Field(...)] ,例如:

    from pydantic import BaseModel, ConstrainedInt
    
    
    class MyInt(ConstrainedInt):
        ge = 0
    
    
    class Model(BaseModel):
        x: MyInt
    

    ...becomes:

    from typing_extensions import Annotated
    
    from pydantic import BaseModel, Field
    
    MyInt = Annotated[int, Field(ge=0)]
    
    
    class Model(BaseModel):
        x: MyInt
    

    Annotated 文档中了解更多关于它的内容。

    对于 ConstrainedStr ,可以使用 StringConstraints 来替代。

    Mypy Plugins

    Pydantic V2 在 pydantic.mypy 中包含一个 mypy 插件。

    使用 V1 功能时可能需要同时启用 pydantic.v1.mypy 插件。

    配置 mypy 插件:

    === mypy.ini

    ```ini
    [mypy]
    plugins = pydantic.mypy, pydantic.v1.mypy # include `.v1.mypy` if required.
    ```
    

    === pyproject.toml

    ```toml
    [tool.mypy]
    plugins = [
        "pydantic.mypy",
        "pydantic.v1.mypy",
    ]
    ```
    

    其他变更

    • 已不再支持 email-validator<2.0.0 。请确保使用 pip install -U email-validator 进行更新。

    迁移到 Pydantic V2

    Pydantic V1 Pydantic V2
    pydantic.BaseSettings pydantic_settings.BaseSettings
    pydantic.color pydantic_extra_types.color
    pydantic.types.PaymentCardBrand pydantic_extra_types.PaymentCardBrand
    pydantic.types.PaymentCardNumber pydantic_extra_types.PaymentCardNumber
    pydantic.utils.version_info pydantic.version.version_info
    pydantic.error_wrappers.ValidationError pydantic.ValidationError
    pydantic.utils.to_camel pydantic.alias_generators.to_pascal
    pydantic.utils.to_lower_camel pydantic.alias_generators.to_camel
    pydantic.PyObject pydantic.ImportString

    已弃用并在 Pydantic V2 中移动

    Pydantic V1 Pydantic V2
    pydantic.tools.schema_of pydantic.deprecated.tools.schema_of
    pydantic.tools.parse_obj_as pydantic.deprecated.tools.parse_obj_as
    pydantic.tools.schema_json_of pydantic.deprecated.tools.schema_json_of
    pydantic.json.pydantic_encoder pydantic.deprecated.json.pydantic_encoder
    pydantic.validate_arguments pydantic.deprecated.decorator.validate_arguments
    pydantic.json.custom_pydantic_encoder pydantic.deprecated.json.custom_pydantic_encoder
    pydantic.json.ENCODERS_BY_TYPE pydantic.deprecated.json.ENCODERS_BY_TYPE
    pydantic.json.timedelta_isoformat pydantic.deprecated.json.timedelta_isoformat
    pydantic.decorator.validate_arguments pydantic.deprecated.decorator.validate_arguments
    pydantic.class_validators.validator pydantic.deprecated.class_validators.validator
    pydantic.class_validators.root_validator pydantic.deprecated.class_validators.root_validator
    pydantic.utils.deep_update pydantic.v1.utils.deep_update
    pydantic.utils.GetterDict pydantic.v1.utils.GetterDict
    pydantic.utils.lenient_issubclass pydantic.v1.utils.lenient_issubclass
    pydantic.utils.lenient_isinstance pydantic.v1.utils.lenient_isinstance
    pydantic.utils.is_valid_field pydantic.v1.utils.is_valid_field
    pydantic.utils.update_not_none pydantic.v1.utils.update_not_none
    pydantic.utils.import_string pydantic.v1.utils.import_string
    pydantic.utils.Representation pydantic.v1.utils.Representation
    pydantic.utils.ROOT_KEY pydantic.v1.utils.ROOT_KEY
    pydantic.utils.smart_deepcopy pydantic.v1.utils.smart_deepcopy
    pydantic.utils.sequence_like pydantic.v1.utils.sequence_like

    已在 Pydantic V2 中移除

    • pydantic.ConstrainedBytes
    • pydantic.ConstrainedDate
    • pydantic.ConstrainedDecimal
    • pydantic.ConstrainedFloat
    • pydantic.ConstrainedFrozenSet
    • pydantic.ConstrainedInt
    • pydantic.ConstrainedList
    • pydantic.ConstrainedSet
    • pydantic.ConstrainedStr
    • pydantic.JsonWrapper
    • pydantic.NoneBytes
      • 这是 None | bytes 的一个别名。

    • pydantic.NoneStr
      • 这是 None | str 的一个别名。

    • pydantic.NoneStrBytes
      • 这是 None | str | bytes 的一个别名。

    • pydantic.Protocol
    • pydantic.Required
    • pydantic.StrBytes
      • 这是 str | bytes 的一个别名。

    • pydantic.compiled
    • pydantic.config.get_config
    • pydantic.config.inherit_config
    • pydantic.config.prepare_config
    • pydantic.create_model_from_namedtuple
    • pydantic.create_model_from_typeddict
    • pydantic.dataclasses.create_pydantic_model_from_dataclass
    • pydantic.dataclasses.make_dataclass_validator
    • pydantic.dataclasses.set_validation
    • pydantic.datetime_parse.parse_date
    • pydantic.datetime_parse.parse_time
    • pydantic.datetime_parse.parse_datetime
    • pydantic.datetime_parse.parse_duration
    • pydantic.error_wrappers.ErrorWrapper
    • pydantic.errors.AnyStrMaxLengthError
    • pydantic.errors.AnyStrMinLengthError
    • pydantic.errors.ArbitraryTypeError
    • pydantic.errors.BoolError
    • pydantic.errors.BytesError
    • pydantic.errors.CallableError
    • pydantic.errors.ClassError
    • pydantic.errors.ColorError
    • pydantic.errors.ConfigError
    • pydantic.errors.DataclassTypeError
    • pydantic.errors.DateError
    • pydantic.errors.DateNotInTheFutureError
    • pydantic.errors.DateNotInThePastError
    • pydantic.errors.DateTimeError
    • pydantic.errors.DecimalError
    • pydantic.errors.DecimalIsNotFiniteError
    • pydantic.errors.DecimalMaxDigitsError
    • pydantic.errors.DecimalMaxPlacesError
    • pydantic.errors.DecimalWholeDigitsError
    • pydantic.errors.DictError
    • pydantic.errors.DurationError
    • pydantic.errors.EmailError
    • pydantic.errors.EnumError
    • pydantic.errors.EnumMemberError
    • pydantic.errors.ExtraError
    • pydantic.errors.FloatError
    • pydantic.errors.FrozenSetError
    • pydantic.errors.FrozenSetMaxLengthError
    • pydantic.errors.FrozenSetMinLengthError
    • pydantic.errors.HashableError
    • pydantic.errors.IPv4AddressError
    • pydantic.errors.IPv4InterfaceError
    • pydantic.errors.IPv4NetworkError
    • pydantic.errors.IPv6AddressError
    • pydantic.errors.IPv6InterfaceError
    • pydantic.errors.IPv6NetworkError
    • pydantic.errors.IPvAnyAddressError
    • pydantic.errors.IPvAnyInterfaceError
    • pydantic.errors.IPvAnyNetworkError
    • pydantic.errors.IntEnumError
    • pydantic.errors.IntegerError
    • pydantic.errors.InvalidByteSize
    • pydantic.errors.InvalidByteSizeUnit
    • pydantic.errors.InvalidDiscriminator
    • pydantic.errors.InvalidLengthForBrand
    • pydantic.errors.JsonError
    • pydantic.errors.JsonTypeError
    • pydantic.errors.ListError
    • pydantic.errors.ListMaxLengthError
    • pydantic.errors.ListMinLengthError
    • pydantic.errors.ListUniqueItemsError
    • pydantic.errors.LuhnValidationError
    • pydantic.errors.MissingDiscriminator
    • pydantic.errors.MissingError
    • pydantic.errors.NoneIsAllowedError
    • pydantic.errors.NoneIsNotAllowedError
    • pydantic.errors.NotDigitError
    • pydantic.errors.NotNoneError
    • pydantic.errors.NumberNotGeError
    • pydantic.errors.NumberNotGtError
    • pydantic.errors.NumberNotLeError
    • pydantic.errors.NumberNotLtError
    • pydantic.errors.NumberNotMultipleError
    • pydantic.errors.PathError
    • pydantic.errors.PathNotADirectoryError
    • pydantic.errors.PathNotAFileError
    • pydantic.errors.PathNotExistsError
    • pydantic.errors.PatternError
    • pydantic.errors.PyObjectError
    • pydantic.errors.PydanticTypeError
    • pydantic.errors.PydanticValueError
    • pydantic.errors.SequenceError
    • pydantic.errors.SetError
    • pydantic.errors.SetMaxLengthError
    • pydantic.errors.SetMinLengthError
    • pydantic.errors.StrError
    • pydantic.errors.StrRegexError
    • pydantic.errors.StrictBoolError
    • pydantic.errors.SubclassError
    • pydantic.errors.TimeError
    • pydantic.errors.TupleError
    • pydantic.errors.TupleLengthError
    • pydantic.errors.UUIDError
    • pydantic.errors.UUIDVersionError
    • pydantic.errors.UrlError
    • pydantic.errors.UrlExtraError
    • pydantic.errors.UrlHostError
    • pydantic.errors.UrlHostTldError
    • pydantic.errors.UrlPortError
    • pydantic.errors.UrlSchemeError
    • pydantic.errors.UrlSchemePermittedError
    • pydantic.errors.UrlUserInfoError
    • pydantic.errors.WrongConstantError
    • pydantic.main.validate_model
    • pydantic.networks.stricturl
    • pydantic.parse_file_as
    • pydantic.parse_raw_as
    • pydantic.stricturl
    • pydantic.tools.parse_file_as
    • pydantic.tools.parse_raw_as
    • pydantic.types.JsonWrapper
    • pydantic.types.NoneBytes
    • pydantic.types.NoneStr
    • pydantic.types.NoneStrBytes
    • pydantic.types.PyObject
    • pydantic.types.StrBytes
    • pydantic.typing.evaluate_forwardref
    • pydantic.typing.AbstractSetIntStr
    • pydantic.typing.AnyCallable
    • pydantic.typing.AnyClassMethod
    • pydantic.typing.CallableGenerator
    • pydantic.typing.DictAny
    • pydantic.typing.DictIntStrAny
    • pydantic.typing.DictStrAny
    • pydantic.typing.IntStr
    • pydantic.typing.ListStr
    • pydantic.typing.MappingIntStrAny
    • pydantic.typing.NoArgAnyCallable
    • pydantic.typing.NoneType
    • pydantic.typing.ReprArgs
    • pydantic.typing.SetStr
    • pydantic.typing.StrPath
    • pydantic.typing.TupleGenerator
    • pydantic.typing.WithArgsTypes
    • pydantic.typing.all_literal_values
    • pydantic.typing.display_as_type
    • pydantic.typing.get_all_type_hints
    • pydantic.typing.get_args
    • pydantic.typing.get_origin
    • pydantic.typing.get_sub_types
    • pydantic.typing.is_callable_type
    • pydantic.typing.is_classvar
    • pydantic.typing.is_finalvar
    • pydantic.typing.is_literal_type
    • pydantic.typing.is_namedtuple
    • pydantic.typing.is_new_type
    • pydantic.typing.is_none_type
    • pydantic.typing.is_typeddict
    • pydantic.typing.is_typeddict_special
    • pydantic.typing.is_union
    • pydantic.typing.new_type_supertype
    • pydantic.typing.resolve_annotations
    • pydantic.typing.typing_base
    • pydantic.typing.update_field_forward_refs
    • pydantic.typing.update_model_forward_refs
    • pydantic.utils.ClassAttribute
    • pydantic.utils.DUNDER_ATTRIBUTES
    • pydantic.utils.PyObjectStr
    • pydantic.utils.ValueItems
    • pydantic.utils.almost_equal_floats
    • pydantic.utils.get_discriminator_alias_and_values
    • pydantic.utils.get_model
    • pydantic.utils.get_unique_discriminator_alias
    • pydantic.utils.in_ipython
    • pydantic.utils.is_valid_identifier
    • pydantic.utils.path_type
    • pydantic.utils.validate_field_name
    • pydantic.validate_model

    本文总阅读量