Lewati ke isi

Serialisasi

Selain mengakses atribut model secara langsung melalui nama fieldnya (misalnya model.foobar ), model dapat dikonversi, dibuang, diserialkan, dan diekspor dalam beberapa cara.

!!! tip "Serialize versus dump" Pydantic menggunakan istilah "serialize" dan "dump" secara bergantian. Keduanya merujuk pada proses mengonversi model ke kamus atau string yang dikodekan JSON.

Outside of Pydantic, the word "serialize" usually refers to converting in-memory data into a string or bytes.
However, in the context of Pydantic, there is a very close relationship between converting an object from a more
structured form — such as a Pydantic model, a dataclass, etc. — into a less structured form comprised of
Python built-ins such as dict.

While we could (and on occasion, do) distinguish between these scenarios by using the word "dump" when converting to
primitives and "serialize" when converting to string, for practical purposes, we frequently use the word "serialize"
to refer to both of these situations, even though it does not always imply conversion to a string or bytes.

model.model_dump(...)

??? api "Dokumentasi API" pydantic.main.BaseModel.model_dump

Ini adalah cara utama untuk mengubah model menjadi kamus. Sub-model akan dikonversi secara rekursif ke kamus.

!!! catatan Satu-satunya pengecualian pada sub-model yang dikonversi ke kamus adalah bahwa RootModel dan subkelasnya akan memiliki nilai bidang root yang dibuang secara langsung, tanpa kamus pembungkus. Ini juga dilakukan secara rekursif.

!!! catatan Anda dapat menggunakan bidang terhitung untuk memasukkan data property dan cached_property dalam keluaran model.model_dump(...) .

Contoh:

from typing import Any, List, Optional

from pydantic import BaseModel, Field, Json


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: Optional[float] = 1.1
    foo: str = Field(serialization_alias='foo_alias')
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

# returns a dictionary:
print(m.model_dump())
#> {'banana': 3.14, 'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
print(m.model_dump(by_alias=True))
#> {'banana': 3.14, 'foo_alias': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_unset=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=1.1, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=None, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_none=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}


class Model(BaseModel):
    x: List[Json[Any]]


print(Model(x=['{"a": 1}', '[1, 2]']).model_dump())
#> {'x': [{'a': 1}, [1, 2]]}
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump(round_trip=True))
#> {'x': ['{"a":1}', '[1,2]']}

model.model_dump_json(...)

??? api "Dokumentasi API" pydantic.main.BaseModel.model_dump_json

Metode .model_dump_json() membuat serial model secara langsung ke string berkode JSON yang setara dengan hasil yang dihasilkan oleh .model_dump() .

Lihat argumen untuk informasi selengkapnya.

!!! catatan Pydantic dapat membuat serialisasi banyak tipe yang umum digunakan ke JSON yang seharusnya tidak kompatibel dengan json.dumps(foobar) sederhana (misalnya datetime , date atau UUID ) .

from datetime import datetime

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    foo: datetime
    bar: BarModel


m = FooBarModel(foo=datetime(2032, 6, 1, 12, 13, 14), bar={'whatever': 123})
print(m.model_dump_json())
#> {"foo":"2032-06-01T12:13:14","bar":{"whatever":123}}
print(m.model_dump_json(indent=2))
"""
{
  "foo": "2032-06-01T12:13:14",
  "bar": {
    "whatever": 123
  }
}
"""

dict(model) dan iterasi

Model Pydantic juga dapat dikonversi ke kamus menggunakan dict(model) , dan Anda juga dapat mengulangi bidang model menggunakan for field_name, field_value in model: . Dengan pendekatan ini, nilai bidang mentah dikembalikan, sehingga submodel tidak akan dikonversi ke kamus.

Contoh:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(dict(m))
#> {'banana': 3.14, 'foo': 'hello', 'bar': BarModel(whatever=123)}
for name, value in m:
    print(f'{name}: {value}')
    #> banana: 3.14
    #> foo: hello
    #> bar: whatever=123

Perhatikan juga bahwa RootModel dapat dikonversi ke kamus dengan kunci 'root' .

Serializer khusus

Pydantic menyediakan beberapa [serialisasi fungsional][pydantic.fungsional_serializers] untuk menyesuaikan cara model diserialkan ke kamus atau JSON.

  • [@field_serializer][pydantic.fungsional_serializers.field_serializer]
  • [@model_serializer][pydantic.fungsional_serializers.model_serializer]
  • [PlainSerializer][pydantic.fungsional_serializers.PlainSerializer]
  • [WrapSerializer][pydantic.fungsional_serializers.WrapSerializer]

Serialisasi dapat dikustomisasi pada bidang menggunakan dekorator [@field_serializer][pydantic.fungsional_serializers.field_serializer], dan pada model menggunakan dekorator [@model_serializer][pydantic.fungsional_serializers.model_serializer].

from datetime import datetime, timedelta, timezone
from typing import Any, Dict

from pydantic import BaseModel, ConfigDict, field_serializer, model_serializer


class WithCustomEncoders(BaseModel):
    model_config = ConfigDict(ser_json_timedelta='iso8601')

    dt: datetime
    diff: timedelta

    @field_serializer('dt')
    def serialize_dt(self, dt: datetime, _info):
        return dt.timestamp()


m = WithCustomEncoders(
    dt=datetime(2032, 6, 1, tzinfo=timezone.utc), diff=timedelta(hours=100)
)
print(m.model_dump_json())
#> {"dt":1969660800.0,"diff":"P4DT4H"}


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> Dict[str, Any]:
        return {'x': f'serialized {self.x}'}


print(Model(x='test value').model_dump_json())
#> {"x":"serialized test value"}

!!! catatan Sebuah serializer tunggal juga dapat dipanggil di semua bidang dengan meneruskan nilai khusus '*' ke dekorator [@field_serializer][pydantic.function_serializers.field_serializer].

Selain itu, [PlainSerializer][pydantic.function_serializers.PlainSerializer] dan [WrapSerializer][pydantic.function_serializers.WrapSerializer] memungkinkan Anda menggunakan fungsi untuk mengubah output serialisasi.

Kedua serializer menerima argumen opsional termasuk:

  • return_type menentukan tipe kembalian untuk fungsi tersebut. Jika dihilangkan maka akan disimpulkan dari anotasi tipe.
  • when_used menentukan kapan serializer ini harus digunakan. Menerima string dengan nilai 'selalu', 'kecuali-tidak ada', 'json', dan 'json-kecuali-tidak ada'. Defaultnya adalah 'selalu'.

PlainSerializer menggunakan fungsi sederhana untuk mengubah output serialisasi.

from typing_extensions import Annotated

from pydantic import BaseModel
from pydantic.functional_serializers import PlainSerializer

FancyInt = Annotated[
    int, PlainSerializer(lambda x: f'{x:,}', return_type=str, when_used='json')
]


class MyModel(BaseModel):
    x: FancyInt


print(MyModel(x=1234).model_dump())
#> {'x': 1234}

print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,234'}

WrapSerializer menerima input mentah bersama dengan fungsi handler yang menerapkan logika serialisasi standar, dan dapat mengubah nilai yang dihasilkan sebelum mengembalikannya sebagai output akhir serialisasi.

from typing import Any

from typing_extensions import Annotated

from pydantic import BaseModel, SerializerFunctionWrapHandler
from pydantic.functional_serializers import WrapSerializer


def ser_wrap(v: Any, nxt: SerializerFunctionWrapHandler) -> str:
    return f'{nxt(v + 1):,}'


FancyInt = Annotated[int, WrapSerializer(ser_wrap, when_used='json')]


class MyModel(BaseModel):
    x: FancyInt


print(MyModel(x=1234).model_dump())
#> {'x': 1234}

print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,235'}

Mengganti tipe pengembalian saat membuang model

Meskipun nilai kembalian .model_dump() biasanya dapat dideskripsikan sebagai dict[str, Any] , melalui penggunaan @model_serializer Anda sebenarnya dapat menyebabkannya mengembalikan nilai yang tidak cocok dengan tanda tangan ini:

from pydantic import BaseModel, model_serializer


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> str:
        return self.x


print(Model(x='not a dict').model_dump())
#> not a dict

Jika Anda ingin melakukan ini dan masih mendapatkan pemeriksaan tipe yang tepat untuk metode ini, Anda dapat mengganti .model_dump() di blok if TYPE_CHECKING: ::

from typing import TYPE_CHECKING, Any

from typing_extensions import Literal

from pydantic import BaseModel, model_serializer


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> str:
        return self.x

    if TYPE_CHECKING:
        # Ensure type checkers see the correct return type
        def model_dump(
            self,
            *,
            mode: Literal['json', 'python'] | str = 'python',
            include: Any = None,
            exclude: Any = None,
            by_alias: bool = False,
            exclude_unset: bool = False,
            exclude_defaults: bool = False,
            exclude_none: bool = False,
            round_trip: bool = False,
            warnings: bool = True,
        ) -> str: ...

Trik ini sebenarnya digunakan di RootModel untuk tujuan ini.

Membuat serial subkelas

Subkelas tipe standar

Subkelas tipe standar secara otomatis dibuang seperti kelas supernya:

from datetime import date, timedelta
from typing import Any, Type

from pydantic_core import core_schema

from pydantic import BaseModel, GetCoreSchemaHandler


class DayThisYear(date):
    """
    Contrived example of a special type of date that
    takes an int and interprets it as a day in the current year
    """

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        return core_schema.no_info_after_validator_function(
            cls.validate,
            core_schema.int_schema(),
            serialization=core_schema.format_ser_schema('%Y-%m-%d'),
        )

    @classmethod
    def validate(cls, v: int):
        return date(2023, 1, 1) + timedelta(days=v)


class FooModel(BaseModel):
    date: DayThisYear


m = FooModel(date=300)
print(m.model_dump_json())
#> {"date":"2023-10-28"}

Instance subkelas untuk bidang BaseModel , kelas data, TypedDict

Saat menggunakan bidang yang anotasinya merupakan tipe mirip struct (misalnya, subkelas BaseModel , kelas data, dll.), perilaku defaultnya adalah membuat serial nilai atribut seolah-olah itu adalah turunan dari tipe yang dianotasi, meskipun itu adalah subkelas. Lebih khusus lagi, hanya bidang dari tipe beranotasi yang akan disertakan dalam objek yang dibuang:

from pydantic import BaseModel


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user: User


user = UserLogin(name='pydantic', password='hunter2')

m = OuterModel(user=user)
print(m)
#> user=UserLogin(name='pydantic', password='hunter2')
print(m.model_dump())  # note: the password field is not included
#> {'user': {'name': 'pydantic'}}

!!! peringatan "Peringatan Migrasi" Perilaku ini berbeda dari cara kerjanya di Pydantic V1, di mana kami akan selalu menyertakan semua bidang (subkelas) saat membuang model ke dikt secara rekursif. Motivasi di balik perubahan perilaku ini adalah untuk membantu memastikan bahwa Anda mengetahui dengan tepat bidang mana yang dapat disertakan saat membuat serial, bahkan jika subkelas diteruskan saat membuat instance objek. Secara khusus, hal ini dapat membantu mencegah kejutan ketika menambahkan informasi sensitif seperti rahasia sebagai bidang subkelas.

Membuat serial dengan mengetik bebek 🦆

Apa itu serialisasi dengan pengetikan bebek?

Duck-typing serialization is the behavior of serializing an object based on the fields present in the object itself, rather than the fields present in the schema of the object. This means that when an object is serialized, fields present in a subclass, but not in the original schema, will be included in the serialized output.

This behavior was the default in Pydantic V1, but was changed in V2 to help ensure that you know precisely which fields would be included when serializing, even if subclasses get passed when instantiating the object. This helps prevent security risks when serializing subclasses with sensitive information, for example.

Jika Anda menginginkan perilaku serialisasi pengetikan bebek gaya v1, Anda dapat menggunakan pengaturan runtime, atau memberi anotasi pada masing-masing tipe.

  • Tingkat bidang/jenis: gunakan anotasi SerializeAsAny
  • Level runtime: gunakan flag serialize_as_any saat memanggil model_dump() atau model_dump_json()

Kami membahas opsi di bawah ini secara lebih rinci:

Anotasi SerializeAsAny :

Jika Anda menginginkan perilaku serialisasi pengetikan bebek, ini dapat dilakukan menggunakan anotasi SerializeAsAny pada tipe:

from pydantic import BaseModel, SerializeAsAny


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    as_any: SerializeAsAny[User]
    as_user: User


user = UserLogin(name='pydantic', password='password')

print(OuterModel(as_any=user, as_user=user).model_dump())
"""
{
    'as_any': {'name': 'pydantic', 'password': 'password'},
    'as_user': {'name': 'pydantic'},
}
"""

Ketika sebuah bidang diberi anotasi sebagai SerializeAsAny[<SomeType>] , perilaku validasi akan sama seperti jika diberi anotasi sebagai <SomeType> , dan pemeriksa tipe seperti mypy akan memperlakukan atribut tersebut sebagai memiliki tipe yang sesuai juga. Namun ketika membuat serial, bidang tersebut akan diserialisasi seolah-olah petunjuk tipe untuk bidang tersebut adalah Any , dari situlah nama tersebut berasal.

serialize_as_any waktu proses apa pun

Pengaturan runtime serialize_as_any dapat digunakan untuk membuat serialisasi data model dengan atau tanpa perilaku serialisasi yang diketik bebek. serialize_as_any dapat diteruskan sebagai argumen kata kunci ke metode model_dump() dan model_dump_json dari BaseModel s dan RootModel s. Itu juga dapat diteruskan sebagai argumen kata kunci ke metode dump_python() dan dump_json() dari TypeAdapter s.

Jika serialize_as_any disetel ke True , model akan diserialkan menggunakan perilaku serialisasi yang diketik bebek, yang berarti model akan mengabaikan skema dan malah menanyakan objek itu sendiri bagaimana seharusnya membuat serialisasi. Secara khusus, ini berarti bahwa ketika subkelas model diserialkan, bidang yang ada di subkelas tetapi tidak ada dalam skema asli akan disertakan.

Jika serialize_as_any diatur ke False (yang merupakan default), model akan diserialkan menggunakan skema, yang berarti bahwa bidang yang ada dalam subkelas tetapi tidak dalam skema asli akan diabaikan.

!!! pertanyaan "Mengapa bendera ini berguna?" Terkadang, Anda ingin memastikan bahwa bidang apa pun yang mungkin telah ditambahkan di subkelas, objek serial hanya akan memiliki bidang yang tercantum dalam definisi tipe asli. Ini bisa berguna jika Anda menambahkan sesuatu seperti bidang password: str di subkelas yang tidak ingin Anda sertakan secara tidak sengaja dalam keluaran serial.

Misalnya:

from pydantic import BaseModel


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user1: User
    user2: User


user = UserLogin(name='pydantic', password='password')

outer_model = OuterModel(user1=user, user2=user)
print(outer_model.model_dump(serialize_as_any=True))  # (1)!
"""
{
    'user1': {'name': 'pydantic', 'password': 'password'},
    'user2': {'name': 'pydantic', 'password': 'password'},
}
"""

print(outer_model.model_dump(serialize_as_any=False))  # (2)!
#> {'user1': {'name': 'pydantic'}, 'user2': {'name': 'pydantic'}}
  1. Dengan serialize_as_any disetel ke True , hasilnya cocok dengan V1.
  2. Dengan serialize_as_any disetel ke False (default V2), bidang yang ada di subkelas, namun bukan kelas dasar, tidak disertakan dalam serialisasi.

Pengaturan ini bahkan berlaku dengan pola bersarang dan rekursif juga. Misalnya:

from typing import List

from pydantic import BaseModel


class User(BaseModel):
    name: str
    friends: List['User']


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user: User


user = UserLogin(
    name='samuel',
    password='pydantic-pw',
    friends=[UserLogin(name='sebastian', password='fastapi-pw', friends=[])],
)

print(OuterModel(user=user).model_dump(serialize_as_any=True))  # (1)!
"""
{
    'user': {
        'name': 'samuel',
        'friends': [
            {'name': 'sebastian', 'friends': [], 'password': 'fastapi-pw'}
        ],
        'password': 'pydantic-pw',
    }
}
"""

print(OuterModel(user=user).model_dump(serialize_as_any=False))  # (2)!
"""
{'user': {'name': 'samuel', 'friends': [{'name': 'sebastian', 'friends': []}]}}
"""
  1. Bahkan instance model User yang disarangkan dibuang dengan bidang unik untuk subkelas User .
  2. Bahkan instans model User yang disarangkan dibuang tanpa bidang unik untuk subkelas User .

!!! catatan Perilaku flag runtime serialize_as_any hampir sama dengan perilaku anotasi SerializeAsAny . Ada beberapa perbedaan kecil yang sedang kami upayakan untuk diselesaikan, namun secara umum, Anda dapat mengharapkan perilaku yang sama dari keduanya. Lihat selengkapnya tentang perbedaan dalam terbitan aktif ini

Mengganti default serialize_as_any (False)

Anda dapat mengganti pengaturan default untuk serialize_as_any dengan mengonfigurasi subkelas BaseModel yang menggantikan argumen default serialize_as_any ke model_dump() dan model_dump_json() , lalu menggunakannya sebagai kelas dasar (bukan pydantic.BaseModel ) untuk model apa pun yang Anda pilih. ingin memiliki perilaku default ini.

Misalnya, Anda dapat melakukan hal berikut jika ingin menggunakan serialisasi pengetikan bebek secara default:

from typing import Any, Dict

from pydantic import BaseModel, SecretStr


class MyBaseModel(BaseModel):
    def model_dump(self, **kwargs) -> Dict[str, Any]:
        return super().model_dump(serialize_as_any=True, **kwargs)

    def model_dump_json(self, **kwargs) -> str:
        return super().model_dump_json(serialize_as_any=True, **kwargs)


class User(MyBaseModel):
    name: str


class UserInfo(User):
    password: SecretStr


class OuterModel(MyBaseModel):
    user: User


u = OuterModel(user=UserInfo(name='John', password='secret_pw'))
print(u.model_dump_json())  # (1)!
#> {"user":{"name":"John","password":"**********"}}
  1. Secara default, model_dump_json akan menggunakan perilaku serialisasi pengetikan bebek, yang berarti kolom password disertakan dalam output.

pickle.dumps(model)

Model Pydantic mendukung pengawetan dan pembongkaran yang efisien.

import pickle

from pydantic import BaseModel


class FooBarModel(BaseModel):
    a: str
    b: int


m = FooBarModel(a='hello', b=123)
print(m)
#> a='hello' b=123
data = pickle.dumps(m)
print(data[:20])
#> b'\x80\x04\x95\x95\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main_'
m2 = pickle.loads(data)
print(m2)
#> a='hello' b=123

Penyertaan dan pengecualian tingkat lanjut

Metode model_dump dan model_dump_json mendukung argumen include dan exclude yang dapat berupa kumpulan atau kamus. Hal ini memungkinkan pemilihan bersarang bidang mana yang akan diekspor:

from pydantic import BaseModel, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr


class Transaction(BaseModel):
    id: str
    user: User
    value: int


t = Transaction(
    id='1234567890',
    user=User(id=42, username='JohnDoe', password='hashedpassword'),
    value=9876543210,
)

# using a set:
print(t.model_dump(exclude={'user', 'value'}))
#> {'id': '1234567890'}

# using a dict:
print(t.model_dump(exclude={'user': {'username', 'password'}, 'value': True}))
#> {'id': '1234567890', 'user': {'id': 42}}

print(t.model_dump(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}

True menunjukkan bahwa kita ingin mengecualikan atau menyertakan seluruh kunci, sama seperti jika kita menyertakannya dalam satu set. Hal ini dapat dilakukan pada tingkat kedalaman apa pun.

Perhatian khusus harus diberikan saat memasukkan atau mengecualikan bidang dari daftar atau tuple submodel atau kamus. Dalam skenario ini, model_dump dan metode terkait mengharapkan kunci bilangan bulat untuk penyertaan atau pengecualian berdasarkan elemen. Untuk mengecualikan field dari setiap anggota daftar atau tupel, kunci kamus '__all__' dapat digunakan, seperti yang ditunjukkan di sini:

import datetime
from typing import List

from pydantic import BaseModel, SecretStr


class Country(BaseModel):
    name: str
    phone_code: int


class Address(BaseModel):
    post_code: int
    country: Country


class CardDetails(BaseModel):
    number: SecretStr
    expires: datetime.date


class Hobby(BaseModel):
    name: str
    info: str


class User(BaseModel):
    first_name: str
    second_name: str
    address: Address
    card_details: CardDetails
    hobbies: List[Hobby]


user = User(
    first_name='John',
    second_name='Doe',
    address=Address(
        post_code=123456, country=Country(name='USA', phone_code=1)
    ),
    card_details=CardDetails(
        number='4212934504460000', expires=datetime.date(2020, 5, 1)
    ),
    hobbies=[
        Hobby(name='Programming', info='Writing code and stuff'),
        Hobby(name='Gaming', info='Hell Yeah!!!'),
    ],
)

exclude_keys = {
    'second_name': True,
    'address': {'post_code': True, 'country': {'phone_code'}},
    'card_details': True,
    # You can exclude fields from specific members of a tuple/list by index:
    'hobbies': {-1: {'info'}},
}

include_keys = {
    'first_name': True,
    'address': {'country': {'name'}},
    'hobbies': {0: True, -1: {'name'}},
}

# would be the same as user.model_dump(exclude=exclude_keys) in this case:
print(user.model_dump(include=include_keys))
"""
{
    'first_name': 'John',
    'address': {'country': {'name': 'USA'}},
    'hobbies': [
        {'name': 'Programming', 'info': 'Writing code and stuff'},
        {'name': 'Gaming'},
    ],
}
"""

# To exclude a field from all members of a nested list or tuple, use "__all__":
print(user.model_dump(exclude={'hobbies': {'__all__': {'info'}}}))
"""
{
    'first_name': 'John',
    'second_name': 'Doe',
    'address': {
        'post_code': 123456,
        'country': {'name': 'USA', 'phone_code': 1},
    },
    'card_details': {
        'number': SecretStr('**********'),
        'expires': datetime.date(2020, 5, 1),
    },
    'hobbies': [{'name': 'Programming'}, {'name': 'Gaming'}],
}
"""

Hal yang sama berlaku untuk metode model_dump_json .

Penyertaan dan pengecualian tingkat model dan bidang

Selain argumen eksplisit exclude dan include yang diteruskan ke metode model_dump dan model_dump_json , kita juga dapat meneruskan argumen exclude: bool langsung ke konstruktor Field :

Menyetel exclude pada konstruktor bidang ( Field(..., exclude=True) ) lebih diprioritaskan daripada exclude / include pada model_dump dan model_dump_json :

from pydantic import BaseModel, Field, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr = Field(..., exclude=True)


class Transaction(BaseModel):
    id: str
    value: int = Field(exclude=True)


t = Transaction(
    id='1234567890',
    value=9876543210,
)

print(t.model_dump())
#> {'id': '1234567890'}
print(t.model_dump(include={'id': True, 'value': True}))  # (1)!
#> {'id': '1234567890'}
  1. value dikecualikan dari output karena dikecualikan di Field .

Meskipun demikian, pengaturan exclude pada konstruktor bidang ( Field(..., exclude=True) ) tidak mengambil prioritas di atas parameter exclude_unset , exclude_none , dan exclude_default pada model_dump dan model_dump_json :

from typing import Optional

from pydantic import BaseModel, Field


class Person(BaseModel):
    name: str
    age: Optional[int] = Field(None, exclude=False)


person = Person(name='Jeremy')

print(person.model_dump())
#> {'name': 'Jeremy', 'age': None}
print(person.model_dump(exclude_none=True))  # (1)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_unset=True))  # (2)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_defaults=True))  # (3)!
#> {'name': 'Jeremy'}
  1. age dikecualikan dari output karena exclude_none disetel ke True , dan age disetel ke None .
  2. age dikecualikan dari output karena exclude_unset disetel ke True , dan age tidak disetel di konstruktor Person.
  3. age dikecualikan dari output karena exclude_defaults disetel ke True , dan age mengambil nilai default None .

Konteks Serialisasi

Anda dapat meneruskan objek konteks ke metode serialisasi yang dapat diakses dari argumen info ke fungsi serializer yang dihias. Ini berguna ketika Anda perlu memperbarui perilaku serialisasi secara dinamis selama runtime. Misalnya, jika Anda ingin sebuah bidang dibuang bergantung pada kumpulan nilai yang diizinkan yang dapat dikontrol secara dinamis, hal ini dapat dilakukan dengan meneruskan nilai yang diizinkan berdasarkan konteks:

from pydantic import BaseModel, SerializationInfo, field_serializer


class Model(BaseModel):
    text: str

    @field_serializer('text')
    def remove_stopwords(self, v: str, info: SerializationInfo):
        context = info.context
        if context:
            stopwords = context.get('stopwords', set())
            v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
        return v


model = Model.model_construct(**{'text': 'This is an example document'})
print(model.model_dump())  # no context
#> {'text': 'This is an example document'}
print(model.model_dump(context={'stopwords': ['this', 'is', 'an']}))
#> {'text': 'example document'}
print(model.model_dump(context={'stopwords': ['document']}))
#> {'text': 'This is an example'}

Demikian pula, Anda dapat menggunakan konteks untuk validasi .

model_copy(...)

??? api "Dokumentasi API" pydantic.main.BaseModel.model_copy

model_copy() memungkinkan model diduplikasi (dengan pembaruan opsional), yang sangat berguna saat bekerja dengan model yang dibekukan.

Contoh:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(m.model_copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar) == id(m.model_copy().bar))
#> True
# normal copy gives the same object reference for bar
print(id(m.bar) == id(m.model_copy(deep=True).bar))
#> False
# deep copy gives a new object reference for `bar`

本文总阅读量