Ga naar inhoud

Serialisatie

Naast het rechtstreeks benaderen van modelattributen via hun veldnamen (bijvoorbeeld model.foobar ), kunnen modellen op een aantal manieren worden geconverteerd, gedumpt, geserialiseerd en geëxporteerd.

!!! tip "Serialiseren versus dumpen" Pydantic gebruikt de termen "serialiseren" en "dump" door elkaar. Beide verwijzen naar het proces van het converteren van een model naar een woordenboek of een JSON-gecodeerde tekenreeks.

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 "API-documentatie" pydantic.main.BaseModel.model_dump

Dit is de belangrijkste manier om een model naar een woordenboek te converteren. Submodellen zullen recursief worden omgezet in woordenboeken.

!!! opmerking De enige uitzondering op submodellen die naar woordenboeken worden geconverteerd, is dat bij RootModel en zijn subklassen de root rechtstreeks wordt gedumpt, zonder een omhullend woordenboek. Ook dit gebeurt recursief.

!!! note U kunt berekende velden gebruiken om property en cached_property gegevens op te nemen in de uitvoer model.model_dump(...) .

Voorbeeld:

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 "API-documentatie" pydantic.main.BaseModel.model_dump_json

De methode .model_dump_json() serialiseert een model rechtstreeks naar een JSON-gecodeerde tekenreeks die equivalent is aan het resultaat dat wordt geproduceerd door .model_dump() .

Zie argumenten voor meer informatie.

!!! opmerking Pydantic kan veel veelgebruikte typen serialiseren naar JSON die anders incompatibel zouden zijn met een eenvoudige json.dumps(foobar) (bijv. datetime , date of 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) en iteratie

Pydantic-modellen kunnen ook worden geconverteerd naar woordenboeken met behulp van dict(model) , en u kunt ook de velden van een model doorlopen met behulp van for field_name, field_value in model: . Met deze aanpak worden de onbewerkte veldwaarden geretourneerd, zodat submodellen niet naar woordenboeken worden geconverteerd.

Voorbeeld:

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

Merk ook op dat RootModel wordt geconverteerd naar een woordenboek met de sleutel 'root' .

Aangepaste serialisatiesystemen

Pydantic biedt verschillende functionele serializers om aan te passen hoe een model wordt geserialiseerd naar een woordenboek of JSON.

Serialisatie kan op een veld worden aangepast met behulp van de decorateur @field_serializer, en op een model met behulp van de decorateur @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"}

!!! note Er kan ook één serialisatiefunctie voor alle velden worden aangeroepen door de speciale waarde '*' door te geven aan de @field_serializer decorateur.

Bovendien kunt u met PlainSerializer en WrapSerializer een functie gebruiken om de uitvoer van serialisatie te wijzigen.

Beide serializers accepteren optionele argumenten, waaronder:

  • return_type specificeert het retourtype voor de functie. Indien dit wordt weggelaten, wordt dit afgeleid uit de typeannotatie.
  • when_used geeft aan wanneer deze serializer moet worden gebruikt. Accepteert een tekenreeks met de waarden 'always', 'unless-none', 'json' en 'json-unless-none'. Standaard ingesteld op 'altijd'.

PlainSerializer gebruikt een eenvoudige functie om de uitvoer van serialisatie te wijzigen.

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 ontvangt de onbewerkte invoer samen met een handlerfunctie die de standaard serialisatielogica toepast, en kan de resulterende waarde wijzigen voordat deze wordt geretourneerd als de uiteindelijke uitvoer van de serialisatie.

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'}

Het retourtype overschrijven bij het dumpen van een model

Hoewel de retourwaarde van .model_dump() gewoonlijk kan worden omschreven als dict[str, Any] , kun je door het gebruik van @model_serializer er feitelijk voor zorgen dat het een waarde retourneert die niet overeenkomt met deze handtekening:

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

Als je dit wilt doen en toch de juiste typecontrole voor deze methode wilt krijgen, kun je .model_dump() overschrijven in een if TYPE_CHECKING: blok:

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: ...

Deze truc wordt in RootModel precies voor dit doel gebruikt.

Serialiseren van subklassen

Subklassen van standaardtypen

Subklassen van standaardtypen worden automatisch gedumpt zoals hun superklassen:

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"}

Subklasse-instanties voor velden van BaseModel , dataclasses, TypedDict

Wanneer velden worden gebruikt waarvan de annotaties zelf structuurachtige typen zijn (bijvoorbeeld BaseModel subklassen, dataklassen, enz.), is het standaardgedrag het serialiseren van de attribuutwaarde alsof het een instantie van het geannoteerde type is, zelfs als het een subklasse is. Meer specifiek worden alleen de velden van het geannoteerde type opgenomen in het gedumpte object:

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'}}

!!! waarschuwing "Migratiewaarschuwing" Dit gedrag is anders dan hoe het werkte in Pydantic V1, waar we altijd alle velden (subklasse) zouden opnemen bij het recursief dumpen van modellen naar dicts. De motivatie achter deze gedragsverandering is dat het ervoor zorgt dat u precies weet welke velden kunnen worden opgenomen bij het serialiseren, zelfs als subklassen worden doorgegeven bij het instantiëren van het object. Dit kan met name verrassingen helpen voorkomen bij het toevoegen van gevoelige informatie zoals geheimen als velden van subklassen.

Serialiseren met duck-typering 🦆

Wat is serialisatie met duck-typering?

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.

Als u serialisatiegedrag bij duck-typing in v1-stijl wilt, kunt u een runtime-instelling gebruiken of afzonderlijke typen annoteren.

  • Veld-/typeniveau: gebruik de annotatie SerializeAsAny
  • Runtimeniveau: gebruik de vlag serialize_as_any bij het aanroepen van model_dump() of model_dump_json()

We bespreken deze opties hieronder in meer detail:

SerializeAsAny -annotatie:

Als u serialisatiegedrag bij duck-typing wilt, kunt u dit doen met behulp van de annotatie SerializeAsAny voor een type:

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'},
}
"""

Wanneer een veld is geannoteerd als SerializeAsAny[<SomeType>] , zal het validatiegedrag hetzelfde zijn als wanneer het geannoteerd zou zijn als <SomeType> , en type-checkers zoals mypy zullen het attribuut ook behandelen alsof het het juiste type heeft. Maar bij het serialiseren wordt het veld geserialiseerd alsof de typehint voor het veld Any is, waar de naam vandaan komt.

serialize_as_any runtime-instelling

De runtime-instelling serialize_as_any kan worden gebruikt om modelgegevens te serialiseren met of zonder serialisatiegedrag van duck-type. serialize_as_any kan als trefwoordargument worden doorgegeven aan de methoden model_dump() en model_dump_json van BaseModel s en RootModel s. Het kan ook als trefwoordargument worden doorgegeven aan de methoden dump_python() en dump_json() van TypeAdapter s.

Als serialize_as_any is ingesteld op True , wordt het model geserialiseerd met behulp van duck-type serialisatiegedrag, wat betekent dat het model het schema negeert en in plaats daarvan aan het object zelf vraagt hoe het moet worden geserialiseerd. Dit betekent in het bijzonder dat wanneer modelsubklassen worden geserialiseerd, velden die aanwezig zijn in de subklasse, maar niet in het oorspronkelijke schema, worden opgenomen.

Als serialize_as_any is ingesteld op False (wat de standaardinstelling is), wordt het model geserialiseerd met behulp van het schema, wat betekent dat velden die aanwezig zijn in een subklasse, maar niet in het oorspronkelijke schema, worden genegeerd.

!!! vraag "Waarom is deze vlag nuttig?" Soms wilt u er zeker van zijn dat, ongeacht welke velden in subklassen zijn toegevoegd, het geserialiseerde object alleen de velden bevat die in de oorspronkelijke typedefinitie zijn vermeld. Dit kan handig zijn als u zoiets als een password: str veld in een subklasse toevoegt dat u niet per ongeluk in de geserialiseerde uitvoer wilt opnemen.

Bijvoorbeeld:

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. Als serialize_as_any is ingesteld op True , komt het resultaat overeen met dat van V1.
  2. Als serialize_as_any is ingesteld op False (de standaardwaarde van V2), worden velden die aanwezig zijn in de subklasse, maar niet in de basisklasse, niet opgenomen in de serialisatie.

Deze instelling wordt zelfs van kracht bij geneste en recursieve patronen. Bijvoorbeeld:

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. Zelfs geneste User worden gedumpt met velden die uniek zijn voor User .
  2. Zelfs geneste User worden gedumpt zonder velden die uniek zijn voor User .

!!! note Het gedrag van de runtimevlag serialize_as_any is vrijwel hetzelfde als het gedrag van de annotatie SerializeAsAny . Er zijn een paar genuanceerde verschillen die we proberen op te lossen, maar voor het grootste deel kun je van beide hetzelfde gedrag verwachten. Zie meer over de verschillen in dit actieve nummer

De standaard serialize_as_any overschrijven (False)

U kunt de standaardinstelling voor serialize_as_any overschrijven door een subklasse van BaseModel te configureren die de standaardinstelling voor het serialize_as_any argument overschrijft naar model_dump() en model_dump_json() , en die vervolgens gebruiken als de basisklasse (in plaats van pydantic.BaseModel ) voor elk model dat u gebruikt wil dit standaardgedrag hebben.

U kunt bijvoorbeeld het volgende doen als u standaard serialisatie van duck-typing wilt gebruiken:

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. model_dump_json maakt standaard gebruik van duck-typing serialisatiegedrag, wat betekent dat het password wordt opgenomen in de uitvoer.

pickle.dumps(model)

Pydantic-modellen ondersteunen efficiënt beitsen en ontbeitsen.

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

Geavanceerd opnemen en uitsluiten

De methoden model_dump en model_dump_json ondersteunen include en exclude van argumenten die sets of woordenboeken kunnen zijn. Hierdoor is een geneste selectie mogelijk van welke velden moeten worden geëxporteerd:

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}}

De True geeft aan dat we een hele sleutel willen uitsluiten of opnemen, net alsof we deze in een set zouden opnemen. Dit kan op elk diepteniveau.

Er moet speciale aandacht worden besteed aan het opnemen of uitsluiten van velden uit een lijst of tupel submodellen of woordenboeken. In dit scenario verwachten model_dump en gerelateerde methoden gehele sleutels voor elementgewijze opname of uitsluiting. Om een veld uit te sluiten van elk lid van een lijst of tupel, kan de woordenboeksleutel '__all__' worden gebruikt, zoals hier weergegeven:

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'}],
}
"""

Hetzelfde geldt voor de model_dump_json methode.

Inclusief en exclusief op model- en veldniveau

Naast de expliciete argumenten exclude en include die worden doorgegeven aan de methoden model_dump en model_dump_json , kunnen we ook de argumenten exclude: bool rechtstreeks doorgeven aan de Field :

Het instellen van exclude op de veldconstructor ( Field(..., exclude=True) ) heeft voorrang op het exclude / include op model_dump en 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 uitgesloten van de uitvoer omdat deze is uitgesloten in Field .

Dat gezegd hebbende, heeft het instellen van exclude op de veldconstructor ( Field(..., exclude=True) ) geen prioriteit boven de parameters exclude_unset , exclude_none en exclude_default op model_dump en 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 uitgesloten van de uitvoer omdat exclude_none is ingesteld op True en age op None is.
  2. age uitgesloten van de uitvoer omdat exclude_unset was ingesteld op True en age niet was ingesteld in de Person-constructor.
  3. age uitgesloten van de uitvoer omdat exclude_defaults is ingesteld op True en age de standaardwaarde None heeft.

Serialisatiecontext

U kunt een contextobject doorgeven aan de serialisatiemethoden die toegankelijk zijn via het info argument voor gedecoreerde serialisatiefuncties. Dit is handig wanneer u het serialisatiegedrag tijdens runtime dynamisch moet bijwerken. Als u bijvoorbeeld wilt dat een veld wordt gedumpt op basis van een dynamisch bestuurbare set toegestane waarden, kunt u dit doen door de toegestane waarden per context door te geven:

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'}

Op dezelfde manier kunt u een context gebruiken voor validatie .

model_copy(...)

??? api "API-documentatie" pydantic.main.BaseModel.model_copy

model_copy() maakt het dupliceren van modellen mogelijk (met optionele updates), wat vooral handig is bij het werken met bevroren modellen.

Voorbeeld:

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`

本文总阅读量