Pydantic V2는 일부 주요 변경 사항을 포함하여 API에 여러 가지 변경 사항을 도입했습니다.
이 페이지는 Pydantic V1에서 Pydantic V2로 코드를 마이그레이션하는 데 도움이 되는 가장 중요한 변경 사항을 강조하는 가이드를 제공합니다.
Pydantic V2 설치¶
Pydantic V2는 이제 Pydantic의 현재 생산 릴리스입니다. PyPI에서 Pydantic V2를 설치할 수 있습니다:
pip install -U pydantic
문제가 발생하면 버그 V2 라벨을 사용하여 GitHub에서 문제를 생성하세요. 이는 오류를 적극적으로 모니터링 및 추적하고 라이브러리 성능을 지속적으로 개선하는 데 도움이 될 것입니다.
어떤 이유로든 최신 Pydantic V1을 사용해야 하는 경우, pydantic.v1에서 설치 및 가져오기에 대한 자세한 내용은 아래 Pydantic V1 기능 계속 사용 섹션을 참조하세요.
코드 변환 도구¶
우리는 귀하의 코드를 마이그레이션하는 데 도움이 되는 도구를 만들었습니다. 이 도구는 아직 베타 버전이지만 코드를 더 빠르게 마이그레이션하는 데 도움이 되기를 바랍니다.
PyPI에서 도구를 설치할 수 있습니다.
pip installump-pydantic
사용법은 간단합니다. 프로젝트 구조가 다음과 같은 경우:
* repo_folder
* my_package
* <python source files> ...
그런 다음 다음을 수행하고 싶을 것입니다.
cd /path/to/repo_folder
bump-pydantic my_package
이에 대한 자세한 내용은 Bump Pydantic 저장소에서 확인하세요.
Pydantic V1 기능을 계속 사용하세요¶
Pydantic V1은 필요할 때 계속 사용할 수 있지만 개선 사항과 새로운 기능을 위해 Pydantic V2로 마이그레이션하는 것이 좋습니다.
최신 Pydantic V1을 사용해야 하는 경우 다음을 사용하여 설치할 수 있습니다.
pip 설치 "pydantic==1.*"
Pydantic V2 패키지는 또한 pydantic.v1을 통해 가져옴으로써 Pydantic V1 API에 대한 액세스를 계속 제공합니다.
예를 들어, Pydantic V2 pydantic.BaseModel 클래스 대신 Pydantic V1의 BaseModel 클래스를 사용할 수 있습니다.
from pydantic.v1 import BaseModel
lenient_isinstance와 같이 Pydantic V2에서 제거된 함수를 가져올 수도 있습니다:
from pydantic.v1.utils import lenient_isinstance
Pydantic V1 문서는 https://docs.pydantic.dev/1.10/에서 확인할 수 있습니다.
v1/v2 환경에서 Pydantic v1 기능 사용¶
pydantic>=1.10.17부터 pydantic.v1 네임스페이스를 V1 내에서 사용할 수 있습니다. 이렇게 하면 pydantic.v1도 지원하는 V2로 마이그레이션하기가 더 쉬워집니다. 네임스페이스. pydantic<2 종속성을 고정 해제하고 V1 기능을 계속 사용하려면 다음 단계를 수행하십시오.
- pydantic<2를 pydantic>=1.10.17로 바꾸세요.
-
다음 항목을 모두 찾아 바꿉니다.
from pydantic.
import
와 함께:
from pydantic.v1.<module> import <object>
귀하의 pydantic 버전에 따라 pydantic v1 기능을 가져오는 방법은 다음과 같습니다:
\=== "pydantic>=1.10.17,<3" v1.10.17부터 .v1 네임스페이스를 V1에서 사용할 수 있으므로 아래와 같이 가져올 수 있습니다.
```python
from pydantic.v1.fields import ModelField
```
\=== "pydantic<3" Pydantic V1 및 V2의 모든 버전은 사용 중인 Pydantic 버전을 모르는 경우를 대비해 다음 가져오기 패턴을 지원합니다:
```python
try:
from pydantic.v1.fields import ModelField
except ImportError:
from pydantic.fields import ModelField
```
!!! note .v1 네임스페이스가 있는 pydantic>=1.10.17,<2를 사용하여 모듈을 가져올 때 이러한 모듈은 .v1이 없는 동일한 가져오기와 동일한 모듈이 아닙니다. 네임스페이스이지만 가져온 기호는 됩니다. 예를 들어 pydantic.v1.fields is not pydantic.fields
하지만 pydantic.v1.fields.ModelField is pydantic.fields.ModelField
. 다행히도 이는 대부분의 경우에는 관련이 없을 것 같습니다. 이는 더 원활한 마이그레이션 환경을 제공하는 데 따른 불행한 결과일 뿐입니다.
마이그레이션 가이드¶
다음 섹션에서는 Pydantic V2의 가장 중요한 변경 사항에 대한 세부 정보를 제공합니다.
pydantic.BaseModel에 대한 변경 사항¶
다양한 메소드 이름이 변경되었습니다. 더 이상 사용되지 않는 모든 BaseModel 메소드는 이제 model_.* 또는 .*pydantic.* 형식과 일치하는 이름을 갖습니다. 가능한 경우 마이그레이션을 쉽게 하기 위해 더 이상 사용되지 않는 메서드를 이전 이름과 함께 유지했지만 해당 메서드를 호출하면 DeprecationWarnings가 발생합니다.
피단틱 V1 | 피단틱 V2 |
---|---|
__전지__ | model_fields |
__private_attributes__ | __pydantic_private__ |
__검증인__ | __pydantic_validator__ |
건설하다() | 모델_구조() |
복사() | 모델_복사() |
사전() | 모델_덤프() |
json_스키마() | 모델_json_스키마() |
json() | 모델_덤프_json() |
pars_obj() | 모델_검증() |
update_forward_refs() | 모델_재구성() |
- 내장된 데이터 로딩 기능 중 일부는 제거될 예정입니다. 특히, parse_raw 및parse_file은 이제 더 이상 사용되지 않습니다. Pydantic V2에서 model_validate_json은parse_raw처럼 작동합니다. 그렇지 않으면 데이터를 로드한 다음 이를 model_validate에 전달해야 합니다.
- from_orm 메소드는 더 이상 사용되지 않습니다. 이제 모델 구성에서 from_attributes=True를 설정한 한 model_validate(Pydantic V1의 parse_obj와 동일)를 사용하여 유사한 결과를 얻을 수 있습니다.
- 모델의 eq 메서드가 변경되었습니다.
- 모델은 다른 BaseModel 인스턴스와만 동일할 수 있습니다.
- 두 모델 인스턴스가 동일하려면 동일해야 합니다.
- 유형(또는 일반 모델의 경우 매개변수화되지 않은 일반 원본 유형)
- 필드 값
- 추가 값(다음 경우에만 관련됨)
model_config['extra'] == 'allow'
) - 개인 속성 값 개인 속성의 값이 다른 모델은 더 이상 동일하지 않습니다.
- 모델은 더 이상 데이터가 포함된 사전과 동일하지 않습니다.
- 다양한 유형의 제네릭이 아닌 모델은 결코 동일하지 않습니다.
- 원본 유형이 다른 일반 모델은 동일하지 않습니다. 예를 들어 MyGenericModel[Any] 인스턴스가 MyGenericModel[int] 인스턴스와 같을 수 있도록 정확한 유형 동일성을 요구하지 않습니다.
- "사용자 정의 루트 모델"을 지정하기 위해 root 필드의 사용을 다음과 같은 새로운 유형으로 대체했습니다. Pydantic V1에서 __root__라는 필드를 사용하는 기능을 대체하기 위한 RootModel입니다. 참고: RootModel 유형은 더 이상 임의_유형_허용을 지원하지 않습니다. 구성 설정. 설명은 이 문제 설명을 참조하세요.
- 우리는 직렬화 사용자 정의와 관련된 Pydantic의 기능을 크게 확장했습니다. 특히, 우리는 @field_serializer를 추가했습니다. @model_serializer 및 각각 다양한 주소를 지정하는 @computed_field 데코레이터 Pydantic V1의 단점.
- 이러한 새로운 데코레이터의 사용법 문서는 사용자 정의 직렬 변환기를 참조하세요.
- 성능 오버헤드와 구현 복잡성으로 인해 이제 지정에 대한 지원이 중단되었습니다. 모델 구성의 json_encoders. 이 기능은 원래 사용자 지정 직렬화 논리를 달성하기 위해 추가되었으며, 우리는 새로운 직렬화 데코레이터가 가장 일반적인 시나리오에서 더 나은 선택이라고 생각합니다.
- 모델의 하위 클래스가 상위 클래스에 중첩된 필드로 나타날 때 직렬화와 관련된 동작을 변경했습니다. 모델. V1에서는 항상 하위 클래스 인스턴스의 모든 필드를 포함합니다. V2에서는 모델을 덤프할 때 주석이 달린 필드 유형에 정의된 필드를 포함합니다. 이는 우발적인 보안을 방지하는 데 도움이 됩니다. 버그. 이에 대한 자세한 내용(이 동작을 선택 해제하는 방법 포함)은 다음에서 확인할 수 있습니다. BaseModel, 데이터 클래스, TypedDict 필드에 대한 하위 클래스 인스턴스 모델 내보내기 문서 섹션.
- GetterDict는 제거된 orm_mode의 구현 세부사항이었기 때문에 제거되었습니다.
- 대부분의 경우 생성자에 전달된 인수는 유효성 검사를 수행하고 필요한 경우 강제 변환을 수행하기 위해 복사됩니다. 이는 변경 가능한 개체를 생성자에 인수로 전달하는 경우에 주목할 수 있습니다. 여기에서 예시와 자세한 내용을 볼 수 있습니다.
- .json() 메서드는 더 이상 사용되지 않으며 다음과 같은 인수와 함께 더 이상 사용되지 않는 이 메서드를 사용하려고 합니다. indent 또는 verify_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())
>¶
V2¶
print(v2_model.model_dump_json())
>¶
Plain json.dumps¶
print(json.dumps(v2_model.model_dump()))
>¶
Modified json.dumps¶
print(json.dumps(v2_model.model_dump(), separators=(',', ':')))
>¶
변경 사항 pydantic.generics.GenericModel
¶
그만큼 pydantic.generics.GenericModel
클래스는 더 이상 필요하지 않아 제거되었습니다. 대신 이제 BaseModel 하위 클래스에 Generic을 상위 클래스로 직접 추가하여 일반 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에서 별칭 속성은 별칭이 설정되지 않은 경우 필드 이름을 반환합니다. Pydantic V2에서는 별칭이 설정되지 않은 경우 None을 반환하도록 이 동작이 변경되었습니다.
필드에서 다음 속성이 제거되거나 변경되었습니다.
const
- min_items(대신 min_length 사용)
- max_items(대신 max_length 사용)
- 고유_항목
- allow_mutation(대신 고정 사용)
- 정규식(대신 패턴 사용)
- final (대신 typing.Final 유형 힌트를 사용하세요)
필드 제약조건은 더 이상 일반 매개변수에 자동으로 적용되지 않습니다. 예를 들어, 다음을 제공하여 목록의 모든 요소가 정규식과 일치하는지 더 이상 확인할 수 없습니다. my_list: list[str] = Field(pattern=".*")
. 대신 typing.Annotated를 사용하여 문자열 자체에 주석을 제공하세요. my_list: list[Annotated[str, Field(pattern=".*")]]
- [해야 할 일: pydantic.Field에 대한 이전 버전과 호환되지 않는 다른 변경 사항을 문서화해야 합니다.]
데이터 클래스 변경 사항¶
Pydantic 데이터 클래스는 BaseModel을 하위 클래스로 분류하지 않고도 표준 데이터 클래스에서 데이터 검증을 활성화하는 데 계속해서 유용합니다. Pydantic V2에서는 이 데이터 클래스 동작에 다음과 같은 변경 사항을 도입했습니다.
- 필드로 사용될 때 데이터 클래스(Pydantic 또는 바닐라)는 더 이상 튜플을 유효성 검사 입력으로 허용하지 않습니다. 대신 dict를 사용해야 합니다.
- Pydantic 데이터 클래스의 __post_init__은 이제 이전이 아닌 검증 후에 호출됩니다.
- 결과적으로 post_init_post_parse 메서드가 중복되어 제거되었습니다.
- Pydantic은 초기화 프로그램에 전달된 추가 필드가 데이터 클래스의 추가 속성으로 저장되는 Pydantic 데이터 클래스에 대해 extra='allow'를 더 이상 지원하지 않습니다. extra='ignore'는 데이터를 구문 분석하는 동안 예기치 않은 필드를 무시하기 위해 계속 지원되지만 해당 필드는 인스턴스에 저장되지 않습니다.
- Pydantic 데이터 클래스에는 더 이상 pydantic_model 속성이 없으며 더 이상 기본 BaseModel을 사용하지 않습니다. 검증을 수행하거나 다른 기능을 제공하기 위해.
- 유효성 검사를 수행하거나 JSON 스키마를 생성하거나 V1에서 __pydantic_model__이 필요할 수 있는 다른 기능을 사용하려면 이제 데이터 클래스를 TypeAdapter(아래에서 자세히 설명)로 래핑하고 사용해야 합니다. 그 방법 중.
- Pydantic V1에서 바닐라(즉, Pydantic이 아닌) 데이터 클래스를 필드로 사용한 경우 상위 유형의 구성은 다음과 같습니다. 데이터 클래스 자체의 구성인 것처럼 사용할 수 있습니다. Pydantic V2에서는 더 이상 그렇지 않습니다.
- Pydantic V2에서는 (BaseModel의 model_config와 마찬가지로) 구성을 재정의하려면 @dataclass 데코레이터의 구성 매개변수를 사용할 수 있습니다. 예시는 데이터 클래스 구성을 참조하세요.
구성 변경 사항¶
-
Pydantic V2에서는 모델에 구성을 지정하려면 model_config라는 클래스 속성을 구성으로 사용하려는 키/값 쌍이 있는 사전으로 설정해야 합니다. Config라는 클래스를 생성하는 Pydantic V1 동작 상위 BaseModel 하위 클래스의 네임스페이스에 있는 는 이제 더 이상 사용되지 않습니다.
-
모델을 서브클래싱할 때 model_config 속성이 상속됩니다. 이는 많은 모델에 대해 지정된 구성으로 기본 클래스를 사용하려는 경우에 유용합니다. MyModel(Model1, Model2) 클래스와 같은 여러 BaseModel 하위 클래스에서 상속하는 경우 두 모델의 model_config 속성에 있는 기본이 아닌 설정이 병합되고 두 모델 모두에 정의된 설정의 경우 Model2의 설정이 해당 설정을 재정의합니다. Model1에서.
-
다음 구성 설정이 제거되었습니다.
- allow_mutation — 이는 제거되었습니다. Frozen을 동등하게 사용할 수 있어야 합니다(현재 사용의 반대).
error_msg_templates
- 필드 — 이는 다양한 버그의 원인이었으므로 제거되었습니다. 필드에 Annotated를 사용하여 원하는 대로 수정할 수 있어야 합니다.
- getter_dict — orm_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_name
→populate_by_name
anystr_lower
→str_to_lower
anystr_strip_whitespace
→str_strip_whitespace
anystr_upper
→str_to_upper
keep_untouched
→ignored_types
max_anystr_length
→str_max_length
min_anystr_length
→str_min_length
orm_mode
→from_attributes
schema_extra
→json_schema_extra
validate_all
→validate_default
See the ConfigDict
API reference for more details.
Changes to validators¶
@validator
and @root_validator
are deprecated¶
@validator
has been deprecated, and should be replaced with@field_validator
, which provides various new features and improvements.- The new
@field_validator
decorator does not have theeach_item
keyword argument; validators you want to apply to items within a generic container should be added by annotating the type argument. See validators in Annotated metadata for details. This looks likeList[Annotated[int, Field(ge=0)]]
- Even if you keep using the deprecated
@validator
decorator, you can no longer add thefield
orconfig
arguments to the signature of validator functions. If you need access to these, you'll need to migrate to@field_validator
— see the next section for more details. - If you use the
always=True
keyword argument to a validator function, note that standard validators for the annotated type will also be applied even to defaults, not just the custom validators. For example, despite the fact that the validator below will never error, the following code raises aValidationError
:
- The new
!!! note To avoid this, you can use the validate_default
argument in the Field
function. When set to True
, it mimics the behavior of always=True
in Pydantic v1. However, the new way of using validate_default
is encouraged as it provides more flexibility and control.
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
has been deprecated, and should be replaced with@model_validator
, which also provides new features and improvements.- Under some circumstances (such as assignment when
model_config['validate_assignment'] is True
), the@model_validator
decorator will receive an instance of the model, not a dict of values. You may need to be careful to handle this case. - Even if you keep using the deprecated
@root_validator
decorator, due to refactors in validation logic, you can no longer run withskip_on_failure=False
(which is the default value of this keyword argument, so must be set explicitly toTrue
).
- Under some circumstances (such as assignment when
Changes to @validator
's allowed signatures¶
In Pydantic V1, functions wrapped by @validator
could receive keyword arguments with metadata about what was being validated. Some of these arguments have been removed from @field_validator
in Pydantic V2:
config
: Pydantic V2's config is now a dictionary instead of a class, which means this argument is no longer backwards compatible. If you need to access the configuration you should migrate to@field_validator
and useinfo.config
.-
field
: this argument used to be aModelField
object, which was a quasi-internal class that no longer exists in Pydantic V2. Most of this information can still be accessed by using the field name frominfo.field_name
to index intocls.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
is no longer converted to ValidationError
in validators¶
Previously, when raising a TypeError
within a validator function, that error would be wrapped into a ValidationError
and, in some cases (such as with FastAPI), these errors might be displayed to end users. This led to a variety of undesirable behavior — for example, calling a function with the wrong signature might produce a user-facing ValidationError
.
However, in Pydantic V2, when a TypeError
is raised in a validator, it is no longer converted into a 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)
This applies to all validation decorators.
Validator behavior changes¶
Pydantic V2 includes some changes to type coercion. For example:
- coercing
int
,float
, andDecimal
values to strings is now optional and disabled by default, see Coerce Numbers to Strings. - iterable of pairs is no longer coerced to a dict.
See the Conversion table for details on Pydantic V2 type coercion defaults.
The allow_reuse
keyword argument is no longer necessary¶
Previously, Pydantic tracked "reused" functions in decorators as this was a common source of mistakes. We did this by comparing the function's fully qualified name (module name + function name), which could result in false positives. The allow_reuse
keyword argument could be used to disable this when it was intentional.
Our approach to detecting repeatedly defined functions has been overhauled to only error for redefinition within a single class, reducing false positives and bringing the behavior more in line with the errors that type checkers and linters would give for defining a method with the same name multiple times in a single class definition.
In nearly all cases, if you were using allow_reuse=True
, you should be able to simply delete that keyword argument and have things keep working as expected.
@validate_arguments
has been renamed to @validate_call
¶
In Pydantic V2, the @validate_arguments
decorator has been renamed to @validate_call
.
In Pydantic V1, the decorated function had various attributes added, such as raw_function
, and validate
(which could be used to validate arguments without actually calling the decorated function). Due to limited use of these attributes, and performance-oriented changes in implementation, we have not preserved this functionality in @validate_call
.
Input types are not preserved¶
In Pydantic V1 we made great efforts to preserve the types of all field inputs for generic collections when they were proper subtypes of the field annotations. For example, given the annotation Mapping[str, int]
if you passed in a collection.Counter()
you'd get a collection.Counter()
as the value.
Supporting this behavior in V2 would have negative performance implications for the general case (we'd have to check types every time) and would add a lot of complexity to validation. Further, even in V1 this behavior was inconsistent and partially broken: it did not work for many types (str
, UUID
, etc.), and for generic collections it's impossible to re-build the original input correctly without a lot of special casing (consider ChainMap
; rebuilding the input is necessary because we need to replace values after validation, e.g. if coercing strings to ints).
In Pydantic V2 we no longer attempt to preserve the input type in all cases; instead, we only promise that the output type will match the type annotations.
Going back to the Mapping
example, we promise the output will be a valid Mapping
, and in practice it will be a plain 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'>
If you want the output type to be a specific type, consider annotating it as such or implementing a custom validator:
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
While we don't promise to preserve input types everywhere, we do preserve them for subclasses of BaseModel
, and for dataclasses:
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))
Changes to Handling of Standard Types¶
Dicts¶
Iterables of pairs (which include empty iterables) no longer pass validation for fields of type dict
.
Unions¶
While union types will still attempt validation of each choice from left to right, they now preserve the type of the input whenever possible, even if the correct type is not the first choice for which the input would pass validation. As a demonstration, consider the following example:
from typing import Union
from pydantic import BaseModel
class Model(BaseModel):
x: Union[int, str]
print(Model(x='1'))
#> x='1'
In Pydantic V1, the printed result would have been x=1
, since the value would pass validation as an int
. In Pydantic V2, we recognize that the value is an instance of one of the cases and short-circuit the standard union validation.
To revert to the non-short-circuiting left-to-right behavior of V1, annotate the union with Field(union_mode='left_to_right')
. See Union Mode for more details.
Required, optional, and nullable fields¶
Pydantic V2 changes some of the logic for specifying whether a field annotated as Optional
is required (i.e., has no default value) or not (i.e., has a default value of None
or any other value of the corresponding type), and now more closely matches the behavior of dataclasses
. Similarly, fields annotated as Any
no longer have a default value of None
.
The following table describes the behavior of field annotations in 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: 선택사항[str] |
Not required, can be None , is None by default | f4: 선택사항[str] = 없음 |
Not required, can be None , is 'abc' by default | f5: 선택사항[str] = 'abc' |
Required, can be any type (including None ) | f6: 모두 |
Not required, can be any type (including None ) | f7: 모두 = 없음 |
!!! note A field annotated as typing.Optional[T]
will be required, and will allow for a value of None
. It does not mean that the field has a default value of None
. (This is a breaking change from V1.)
!!! note Any default value if provided makes a field not required.
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]
"""
Patterns / regex on strings¶
Pydantic V1 used Python's regex library. Pydantic V2 uses the Rust regex crate. This crate is not just a "Rust version of regular expressions", it's a completely different approach to regular expressions. In particular, it promises linear time searching of strings in exchange for dropping a couple of features (namely look arounds and backreferences). We believe this is a tradeoff worth making, in particular because Pydantic is used to validate untrusted input where ensuring things don't accidentally run in exponential time depending on the untrusted input is important. On the flipside, for anyone not using these features complex regex validation should be orders of magnitude faster because it's done in Rust and in linear time.
If you still want to use Python's regex library, you can use the regex_engine
config setting.
Introduction of TypeAdapter
¶
Pydantic V1 had weak support for validating or serializing non-BaseModel
types.
To work with them, you had to either create a "root" model or use the utility functions in pydantic.tools
(namely, parse_obj_as
and schema_of
).
In Pydantic V2 this is a lot easier: the TypeAdapter
class lets you create an object with methods for validating, serializing, and producing JSON schemas for arbitrary types. This serves as a complete replacement for parse_obj_as
and schema_of
(which are now deprecated), and also covers some of the use cases of "root" models. (RootModel
, discussed above, covers the others.)
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'}
Due to limitations of inferring generic types with common type checkers, to get proper typing in some scenarios, you may need to explicitly specify the generic parameter:
from pydantic import TypeAdapter
adapter = TypeAdapter[str | int](str | int)
...
See Type Adapter for more information.
Defining custom types¶
We have completely overhauled the way custom types are defined in pydantic.
We have exposed hooks for generating both pydantic-core
and JSON schemas, allowing you to get all the performance benefits of Pydantic V2 even when using your own custom types.
We have also introduced ways to use typing.Annotated
to add custom validation to your own types.
The main changes are:
__get_validators__
should be replaced with__get_pydantic_core_schema__
. See Custom Data Types for more information.__modify_schema__
becomes__get_pydantic_json_schema__
. See JSON Schema Customization for more information.
Additionally, you can use typing.Annotated
to modify or provide the __get_pydantic_core_schema__
and __get_pydantic_json_schema__
functions of a type by annotating it, rather than modifying the type itself. This provides a powerful and flexible mechanism for integrating third-party types with Pydantic, and in some cases may help you remove hacks from Pydantic V1 introduced to work around the limitations for custom types.
See Custom Data Types for more information.
Changes to JSON schema generation¶
We received many requests over the years to make changes to the JSON schemas that pydantic generates.
In Pydantic V2, we have tried to address many of the common requests:
- The JSON schema for
Optional
fields now indicates that the valuenull
is allowed. - The
Decimal
type is now exposed in JSON schema (and serialized) as a string. - The JSON schema no longer preserves namedtuples as namedtuples.
- The JSON schema we generate by default now targets draft 2020-12 (with some OpenAPI extensions).
- When they differ, you can now specify if you want the JSON schema representing the inputs to validation, or the outputs from serialization.
However, there have been many reasonable requests over the years for changes which we have not chosen to implement.
In Pydantic V1, even if you were willing to implement changes yourself, it was very difficult because the JSON schema generation process involved various recursive function calls; to override one, you'd have to copy and modify the whole implementation.
In Pydantic V2, one of our design goals was to make it easier to customize JSON schema generation. To this end, we have introduced the class GenerateJsonSchema
, which implements the translation of a type's pydantic-core schema into a JSON schema. By design, this class breaks the JSON schema generation process into smaller methods that can be easily overridden in subclasses to modify the "global" approach to generating JSON schema.
The various methods that can be used to produce JSON schema (such as BaseModel.model_json_schema
or TypeAdapter.json_schema
) accept a keyword argument schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema
, and you can pass your custom subclass to these methods in order to use your own approach to generating JSON schema.
Hopefully this means that if you disagree with any of the choices we've made, or if you are reliant on behaviors in Pydantic V1 that have changed in Pydantic V2, you can use a custom schema_generator
, modifying the GenerateJsonSchema
class as necessary for your application.
BaseSettings
has moved to pydantic-settings
¶
BaseSettings
, the base object for Pydantic settings management, has been moved to a separate package, pydantic-settings
.
Also, the parse_env_var
classmethod has been removed. So, you need to customise settings sources to have your own parsing function.
Color and Payment Card Numbers moved to pydantic-extra-types
¶
The following special-use types have been moved to the Pydantic Extra Types package, which may be installed separately if needed.
Url and Dsn types in pydantic.networks
no longer inherit from str
¶
In Pydantic V1 the AnyUrl
type inherited from str
, and all the other Url
and Dsn
types inherited from these. In Pydantic V2 these types are built on two new Url
and MultiHostUrl
classes using Annotated
.
Inheriting from str
had upsides and downsides, and for V2 we decided it would be better to remove this. To use these types in APIs which expect str
you'll now need to convert them (with str(url)
).
Pydantic V2 uses Rust's Url crate for URL validation. Some of the URL validation differs slightly from the previous behavior in V1. One notable difference is that the new Url
types append slashes to the validated version if no path is included, even if a slash is not specified in the argument to a Url
type constructor. See the example below for this behavior:
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/'
If you still want to use the old behavior without the appended slash, take a look at this solution.
Constrained types¶
The Constrained*
classes were removed, and you should replace them by Annotated[<type>, Field(...)]
, for example:
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
Read more about it in the Composing types via Annotated
docs.
For ConstrainedStr
you can use StringConstraints
instead.
Mypy Plugins¶
Pydantic V2 contains a mypy plugin in pydantic.mypy
.
When using V1 features the pydantic.v1.mypy
plugin might need to also be enabled.
To configure the mypy
plugins:
\=== 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",
]
```
Other changes¶
- Dropped support for
email-validator<2.0.0
. Make sure to update usingpip install -U email-validator
.
Moved in Pydantic V2¶
Pydantic V1 | Pydantic V2 |
---|---|
pydantic.BaseSettings | pydantic_settings.BaseSettings |
pydantic.color | [pydantic_extra_types.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.version.version_info] |
pydantic.error_wrappers.ValidationError | [pydantic.ValidationError ][pydantic_core.ValidationError] |
pydantic.utils.to_camel | [pydantic.alias_generators.to_pascal ][pydantic.alias_generators.to_pascal] |
pydantic.utils.to_lower_camel | [pydantic.alias_generators.to_camel ][pydantic.alias_generators.to_camel] |
pydantic.PyObject | [pydantic.ImportString ][pydantic.types.ImportString] |
Deprecated and moved in 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 |
Removed in Pydantic V2¶
pydantic.ConstrainedBytes
pydantic.ConstrainedDate
pydantic.ConstrainedDecimal
pydantic.ConstrainedFloat
pydantic.ConstrainedFrozenSet
pydantic.ConstrainedInt
pydantic.ConstrainedList
pydantic.ConstrainedSet
pydantic.ConstrainedStr
pydantic.JsonWrapper
pydantic.NoneBytes
- This was an alias to
None | bytes
.
- This was an alias to
pydantic.NoneStr
- This was an alias to
None | str
.
- This was an alias to
pydantic.NoneStrBytes
- This was an alias to
None | str | bytes
.
- This was an alias to
- pydantic.프로토콜
- pydantic.필수
pydantic.StrBytes
- This was an alias to
str | bytes
.
- This was an alias to
- pydantic.컴파일됨
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
本文总阅读量次