Pydantic V2 memperkenalkan sejumlah perubahan pada API, termasuk beberapa perubahan yang dapat menyebabkan gangguan.
Halaman ini memberikan panduan yang menyoroti perubahan paling penting untuk membantu Anda memigrasikan kode Anda dari Pydantic V1 ke Pydantic V2.
Instal Pydantic V2¶
Pydantic V2 sekarang merupakan rilis produksi Pydantic saat ini. Anda dapat menginstal Pydantic V2 dari PyPI:
instalasi pip -U pydantic
Jika Anda mengalami masalah apa pun, buatlah masalah di GitHub menggunakan label bug V2. Hal ini akan membantu kami untuk secara aktif memantau dan melacak kesalahan, dan untuk terus meningkatkan kinerja perpustakaan.
Jika Anda perlu menggunakan Pydantic V1 terbaru karena alasan apa pun, lihat bagian Lanjutkan menggunakan fitur Pydantic V1 di bawah untuk detail tentang instalasi dan impor dari pydantic.v1.
Alat transformasi kode¶
Kami telah membuat alat untuk membantu Anda memigrasikan kode Anda. Alat ini masih dalam versi beta, namun kami berharap alat ini dapat membantu Anda memigrasikan kode dengan lebih cepat.
Anda dapat menginstal alat dari PyPI:
pip instal bump-pydantic
Penggunaannya sederhana. Jika struktur proyek Anda adalah:
* repo_folder
* my_package
* <python source files> ...
Maka Anda ingin melakukan:
cd /path/to/repo_folder
bump-pydantic my_package
Lihat selengkapnya di repositori Bump Pydantic.
Lanjutkan menggunakan fitur Pydantic V1¶
Pydantic V1 masih tersedia saat Anda membutuhkannya, meskipun kami menyarankan untuk bermigrasi ke Pydantic V2 untuk peningkatan dan fitur barunya.
Jika Anda perlu menggunakan Pydantic V1 terbaru, Anda dapat menginstalnya dengan:
instalasi pip "pydantic==1.*"
Paket Pydantic V2 juga terus menyediakan akses ke API Pydantic V1 dengan mengimpor melalui pydantic.v1.
Misalnya, Anda dapat menggunakan kelas BaseModel dari Pydantic V1 alih-alih kelas Pydantic V2 pydantic.BaseModel:
from pydantic.v1 import BaseModel
Anda juga dapat mengimpor fungsi yang telah dihapus dari Pydantic V2, seperti lenient_isinstance:
from pydantic.v1.utils import lenient_isinstance
Dokumentasi Pydantic V1 tersedia di https://docs.pydantic.dev/1.10/.
Menggunakan fitur Pydantic v1 di lingkungan v1/v2¶
Mulai pydantic>=1.10.17, namespace pydantic.v1 dapat digunakan dalam V1. Hal ini mempermudah migrasi ke V2, yang juga mendukung pydantic.v1 ruang nama. Untuk melepaskan sematan ketergantungan pydantic<2 dan terus menggunakan fitur V1, lakukan langkah-langkah berikut:
- Ganti pydantic<2 dengan pydantic>=1.10.17
-
Temukan dan ganti semua kemunculan:
from pydantic.
import
dengan:
from pydantic.v1.<module> import <object>
Inilah cara Anda dapat mengimpor fitur v1 pydantic berdasarkan versi pydantic Anda:
\=== "pydantic>=1.10.17,<3" Pada v1.10.17 namespace .v1 tersedia di V1, memungkinkan impor seperti di bawah ini:
```python
from pydantic.v1.fields import ModelField
```
\=== "pydantic<3" Semua versi Pydantic V1 dan V2 mendukung pola impor berikut, jika Anda tidak tahu versi Pydantic mana yang Anda gunakan:
```python
try:
from pydantic.v1.fields import ModelField
except ImportError:
from pydantic.fields import ModelField
```
!!! catatan Saat mengimpor modul menggunakan pydantic>=1.10.17,<2 dengan namespace .v1, modul ini tidak akan menjadi modul yang sama dengan impor yang sama tanpa .v1 namespace, tetapi simbol yang diimpor akan tetap demikian. Misalnya pydantic.v1.fields is not pydantic.fields
Tetapi pydantic.v1.fields.ModelField is pydantic.fields.ModelField
. Untungnya, hal ini mungkin tidak relevan pada sebagian besar kasus. Ini hanyalah konsekuensi yang disayangkan karena memberikan pengalaman migrasi yang lebih lancar.
Panduan migrasi¶
Bagian berikut memberikan detail tentang perubahan terpenting di Pydantic V2.
Perubahan pada pydantic.BaseModel¶
Berbagai nama metode telah diubah; semua metode BaseModel yang tidak digunakan lagi sekarang memiliki nama yang cocok dengan format model_.* atau .*pydantic.*. Jika memungkinkan, kami tetap mempertahankan metode yang tidak digunakan lagi dengan nama lamanya untuk membantu memudahkan migrasi, namun memanggilnya akan memunculkan DeprecationWarnings.
Pydantic V1 | Pydantic V2 |
---|---|
__bidang__ | model_fields |
__atribut_pribadi__ | __pydantic_pribadi__ |
__validator__ | __pydantic_validator__ |
membangun() | model_konstruksi() |
menyalin() | model_salinan() |
dikte() | model_dump() |
json_skema() | model_json_schema() |
json() | model_dump_json() |
parse_obj() | model_validasi() |
perbarui_forward_refs() | model_rebuild() |
- Beberapa fungsi pemuatan data bawaan telah dijadwalkan untuk dihapus. Secara khusus, parse_raw dan parse_file sekarang tidak digunakan lagi. Di Pydantic V2, model_validate_json berfungsi seperti parse_raw. Jika tidak, Anda harus memuat data dan meneruskannya ke model_validate.
- Metode from_orm sudah tidak digunakan lagi; Anda sekarang dapat menggunakan model_validate (setara dengan parse_obj dari Pydantic V1) untuk mencapai sesuatu yang serupa, selama Anda telah menyetel from_attributes=True di konfigurasi model.
- Metode eq telah berubah untuk model.
- Model hanya bisa sama dengan instance BaseModel lainnya.
- Agar dua contoh model menjadi sama, keduanya harus memiliki hal yang sama:
- Tipe (atau, dalam kasus model generik, tipe asal generik non-parametrik)
- Nilai bidang
- Nilai ekstra (hanya relevan jika
model_config['extra'] == 'allow'
) - Nilai atribut pribadi; model dengan nilai atribut privat yang berbeda tidak lagi sama.
- Model tidak lagi sama dengan dikte yang memuat datanya.
- Model non-generik dari tipe yang berbeda tidak pernah sama.
- Model generik dengan tipe asal berbeda tidak pernah sama. Kami tidak memerlukan persamaan tipe yang tepat sehingga, misalnya, instance MyGenericModel[Any] bisa sama dengan instance MyGenericModel[int].
- Kami telah mengganti penggunaan kolom root untuk menentukan "model root khusus" dengan tipe baru yang disebut RootModel yang dimaksudkan untuk menggantikan fungsionalitas penggunaan bidang bernama root di Pydantic V1. Catatan, tipe RootModel tidak lagi mendukung arbitrer_types_allowed pengaturan konfigurasi. Lihat komentar masalah ini untuk penjelasannya.
- Kami telah memperluas kemampuan Pydantic secara signifikan terkait dengan penyesuaian serialisasi. Secara khusus, kami punya menambahkan @field_serializer, @model_serializer, dan @computed_field dekorator, yang masing-masing alamatnya berbeda-beda kekurangan dari Pydantic V1.
- Lihat Serializer khusus untuk dokumen penggunaan dekorator baru ini.
- Karena overhead kinerja dan kompleksitas implementasi, kami kini tidak lagi mendukung penentuan json_encoders dalam konfigurasi model. Fungsionalitas ini awalnya ditambahkan untuk tujuan mencapai logika serialisasi khusus, dan menurut kami dekorator serialisasi baru adalah pilihan yang lebih baik di sebagian besar skenario umum.
- Kami telah mengubah perilaku yang terkait dengan serialisasi subkelas model ketika muncul sebagai bidang bersarang di induk model. Di V1, kami akan selalu menyertakan semua bidang dari instance subkelas. Di V2, saat kita membuang model, kita hanya menyertakan bidang yang ditentukan pada jenis bidang yang dianotasi. Ini membantu mencegah keamanan yang tidak disengaja bug. Anda dapat membaca lebih lanjut tentang hal ini (termasuk cara menyisih dari perilaku ini) di Instance subkelas untuk bidang BaseModel, kelas data, TypedDict bagian dari model yang mengekspor dokumen.
- GetterDict telah dihapus karena ini hanyalah detail implementasi orm_mode, yang telah dihapus.
- Dalam banyak kasus, argumen yang diteruskan ke konstruktor akan disalin untuk melakukan validasi dan, jika perlu, pemaksaan. Hal ini penting dalam kasus meneruskan objek yang bisa berubah sebagai argumen ke konstruktor. Anda dapat melihat contoh + lebih detail di sini.
- Metode .json() tidak digunakan lagi, dan mencoba menggunakan metode yang tidak digunakan lagi ini dengan argumen seperti indentasi atau sure_ascii dapat menyebabkan kesalahan yang membingungkan. Untuk hasil terbaik, beralihlah ke yang setara dengan V2, model_dump_json(). Jika Anda masih ingin menggunakan argumen tersebut, Anda dapat menggunakan solusi ini.
-
Serialisasi JSON dari nilai kunci non-string umumnya dilakukan dengan str(kunci), yang menyebabkan beberapa perubahan perilaku seperti berikut:
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}}¶
-
hasil model_dump_json() dipadatkan untuk menghemat ruang, dan tidak selalu sama persis dengan keluaran json.dumps(). Meskipun demikian, Anda dapat dengan mudah memodifikasi pemisah yang digunakan dalam hasil json.dumps() untuk menyelaraskan kedua keluaran:
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=(',', ':')))
>¶
Perubahan menjadi pydantic.generics.GenericModel
¶
Itu pydantic.generics.GenericModel
kelas tidak lagi diperlukan, dan telah dihapus. Sebagai gantinya, Anda kini dapat membuat subkelas BaseModel generik hanya dengan menambahkan Generik sebagai kelas induk pada subkelas BaseModel secara langsung. Ini sepertinya class MyGenericModel(BaseModel, Generic[T]): ...
.
Pencampuran model V1 dan V2 tidak didukung yang berarti bahwa parameter tipe BaseModel (V2) generik tersebut tidak dapat menjadi model V1.
Meskipun hal ini mungkin tidak menimbulkan kesalahan, kami sangat menyarankan untuk tidak menggunakan obat generik berparametri dalam pemeriksaan isinstance.
- Misalnya, Anda sebaiknya tidak melakukannya
isinstance(my_model, MyGenericModel[int])
. Namun, hal itu boleh saja dilakukanisinstance(my_model, MyGenericModel)
. (Perhatikan bahwa untuk obat generik standar, akan menimbulkan kesalahan jika melakukan pemeriksaan subkelas dengan obat generik berparameter.) - Jika Anda perlu melakukan pemeriksaan isinstance terhadap obat generik berparametri, Anda dapat melakukannya dengan membuat subkelas kelas generik berparametri. Ini sepertinya
class MyIntModel(MyGenericModel[int]): ...
Danisinstance(my_model, MyIntModel)
.
Temukan informasi selengkapnya di dokumentasi model Generik.
Perubahan pada pydantic.Field¶
Bidang tidak lagi mendukung argumen kata kunci arbitrer untuk ditambahkan ke skema JSON. Sebaliknya, data tambahan apa pun yang ingin Anda tambahkan ke skema JSON harus diteruskan sebagai kamus ke argumen kata kunci json_schema_extra.
Di Pydantic V1, properti alias mengembalikan nama bidang ketika tidak ada alias yang disetel. Di Pydantic V2, perilaku ini telah berubah untuk mengembalikan Tidak Ada ketika tidak ada alias yang disetel.
Properti berikut telah dihapus atau diubah di Bidang:
- konstanta
- min_items (gunakan min_length sebagai gantinya)
- max_items (gunakan max_length sebagai gantinya)
- item_unik
- izinkan_mutasi (gunakan beku sebagai gantinya)
- regex (gunakan pola sebagai gantinya)
- final (gunakan petunjuk tipe typing.Final sebagai gantinya)
Batasan bidang tidak lagi secara otomatis diturunkan ke parameter generik. Misalnya, Anda tidak dapat lagi memvalidasi setiap elemen daftar yang cocok dengan ekspresi reguler dengan menyediakan my_list: list[str] = Field(pattern=".*")
. Sebagai gantinya, gunakan typing.Annotated untuk memberikan anotasi pada str itu sendiri: my_list: list[Annotated[str, Field(pattern=".*")]]
- [TODO: Perlu mendokumentasikan perubahan lain yang tidak kompatibel dengan pydantic.Field]
Perubahan pada kelas data¶
Kelas data Pydantic terus berguna untuk mengaktifkan validasi data pada kelas data standar tanpa harus membuat subkelas BaseModel. Pydantic V2 memperkenalkan perubahan berikut pada perilaku kelas data ini:
- Saat digunakan sebagai kolom, kelas data (Pydantic atau Vanilla) tidak lagi menerima tupel sebagai input validasi; dicts harus digunakan sebagai gantinya.
- post_init di kelas data Pydantic sekarang akan dipanggil setelah validasi, bukan sebelumnya.
- Akibatnya, metode post_init_post_parse menjadi mubazir, sehingga telah dihapus.
- Pydantic tidak lagi mendukung extra='allow' untuk kelas data Pydantic, di mana bidang tambahan yang diteruskan ke penginisialisasi akan disimpan sebagai atribut tambahan di kelas data. extra='ignore' masih didukung untuk tujuan mengabaikan bidang yang tidak diharapkan saat menguraikan data, hanya saja bidang tersebut tidak akan disimpan di instance.
- Kelas data Pydantic tidak lagi memiliki atribut pydantic_model, dan tidak lagi menggunakan BaseModel yang mendasarinya untuk melakukan validasi atau menyediakan fungsionalitas lainnya.
- Untuk melakukan validasi, membuat skema JSON, atau memanfaatkan fungsionalitas lain yang mungkin memerlukan pydantic_model di V1, Anda sekarang harus menggabungkan kelas data dengan TypeAdapter (dibahas lebih lanjut di bawah) dan memanfaatkannya metodenya.
- Di Pydantic V1, jika Anda menggunakan kelas data vanilla (yaitu, non-Pydantic) sebagai bidang, konfigurasi tipe induknya akan digunakan seolah-olah itu adalah konfigurasi untuk kelas data itu sendiri juga. Di Pydantic V2, hal ini tidak lagi terjadi.
- Di Pydantic V2, untuk mengganti konfigurasi (seperti yang Anda lakukan dengan model_config pada BaseModel), Anda dapat menggunakan parameter config pada dekorator @dataclass. Lihat Konfigurasi Dataclass untuk contohnya.
Perubahan pada konfigurasi¶
-
Di Pydantic V2, untuk menentukan konfigurasi pada model, Anda harus menyetel atribut kelas yang disebut model_config menjadi dict dengan pasangan kunci/nilai yang ingin Anda gunakan sebagai konfigurasi. Perilaku Pydantic V1 untuk membuat kelas yang disebut Config di namespace subkelas BaseModel induk kini tidak digunakan lagi.
-
Saat membuat subkelas model, atribut model_config diwarisi. Ini berguna jika Anda ingin menggunakan kelas dasar dengan konfigurasi tertentu untuk banyak model. Catatan, jika Anda mewarisi beberapa subkelas BaseModel, seperti kelas MyModel(Model1, Model2), pengaturan non-default dalam atribut model_config dari kedua model akan digabungkan, dan untuk pengaturan apa pun yang ditentukan di keduanya, pengaturan dari Model2 akan menggantikan pengaturan tersebut dari Model1.
-
Pengaturan konfigurasi berikut telah dihapus:
- izinkan_mutasi — ini telah dihapus. Anda harus dapat menggunakan frozen secara setara (kebalikan dari penggunaan saat ini).
error_msg_templates
- field — ini adalah sumber berbagai bug, jadi telah dihapus. Anda harus dapat menggunakan bidang Beranotasi pada untuk mengubahnya sesuai keinginan.
- getter_dict — orm_mode telah dihapus, dan detail implementasi ini tidak lagi diperlukan.
smart_union
.- underscore_attrs_are_private — perilaku Pydantic V2 sekarang sama seperti jika selalu disetel ke True di Pydantic V1.
json_loads
json_dumps
copy_on_model_validation
post_init_call
-
Pengaturan konfigurasi berikut telah diubah namanya:
-
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: Opsional[str] |
Not required, can be None , is None by default | f4: Opsional[str] = Tidak ada |
Not required, can be None , is 'abc' by default | f5: Opsional[str] = 'abc' |
Required, can be any type (including None ) | f6: Apa saja |
Not required, can be any type (including None ) | f7: Apa saja = Tidak ada |
!!! 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.Representasi | 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.Protokol
- pydantic.Diperlukan
pydantic.StrBytes
- This was an alias to
str | bytes
.
- This was an alias to
- pydantic.dikompilasi
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.mengetik.DictStrAny
- pydantic.mengetik.IntStr
- pydantic.mengetik.ListStr
pydantic.typing.MappingIntStrAny
pydantic.typing.NoArgAnyCallable
pydantic.typing.NoneType
pydantic.typing.ReprArgs
- pydantic.mengetik.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.mengetik.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
本文总阅读量次