Zum Inhalt

Serialisierung

Über den direkten Zugriff auf Modellattribute über ihre Feldnamen (z. B. model.foobar ) hinaus können Modelle auf verschiedene Arten konvertiert, ausgegeben, serialisiert und exportiert werden.

!!! Tipp „Serialisieren versus Dump“ Pydantic verwendet die Begriffe „Serialisieren“ und „Dump“ synonym. Beide beziehen sich auf den Prozess der Konvertierung eines Modells in ein Wörterbuch oder eine JSON-codierte Zeichenfolge.

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-Dokumentation“ pydantic.main.BaseModel.model_dump

Dies ist die primäre Methode zum Konvertieren eines Modells in ein Wörterbuch. Untermodelle werden rekursiv in Wörterbücher umgewandelt.

!!! Hinweis Die einzige Ausnahme bei der Konvertierung von Untermodellen in Wörterbücher besteht darin, dass RootModel und seine Unterklassen den root direkt ohne ein umschließendes Wörterbuch ausgeben. Dies geschieht ebenfalls rekursiv.

!!! Hinweis Sie können berechnete Felder verwenden, um property und cached_property in die Ausgabe model.model_dump(...) einzuschließen.

Beispiel:

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-Dokumentation“ pydantic.main.BaseModel.model_dump_json

Die Methode .model_dump_json() serialisiert ein Modell direkt in eine JSON-codierte Zeichenfolge, die dem von .model_dump() erzeugten Ergebnis entspricht.

Weitere Informationen finden Sie unter arguments.

!!! Hinweis: Pydantic kann viele häufig verwendete Typen in JSON serialisieren, die sonst mit einem einfachen json.dumps(foobar) nicht kompatibel wären (z. B. datetime , date oder 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) und Iteration

Pydantic-Modelle können auch mit dict(model) in Wörterbücher konvertiert werden, und Sie können mit auch über die Felder eines Modells iterieren for field_name, field_value in model: . Bei diesem Ansatz werden die Rohfeldwerte zurückgegeben, sodass Untermodelle nicht in Wörterbücher konvertiert werden.

Beispiel:

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

Beachten Sie auch, dass RootModel mit dem Schlüssel 'root' in ein Wörterbuch konvertiert wird .

Benutzerdefinierte Serialisierer

Pydantic bietet mehrere [funktionale Serialisierer][pydantic.Functional_Serializers], um anzupassen, wie ein Modell in ein Wörterbuch oder JSON serialisiert wird.

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

Die Serialisierung kann für ein Feld mit dem Dekorator [@field_serializer][pydantic.Functional_serializers.field_serializer] und für ein Modell mit dem Dekorator [@model_serializer][pydantic.Functional_serializers.model_serializer] angepasst werden.

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

!!! Hinweis Ein einzelner Serializer kann auch für alle Felder aufgerufen werden, indem der Sonderwert „*“ an den Dekorator [@field_serializer][pydantic.Functional_serializers.field_serializer] übergeben wird.

Darüber hinaus ermöglichen Ihnen [PlainSerializer][pydantic.Functional_serializers.PlainSerializer] und [WrapSerializer][pydantic.Functional_serializers.WrapSerializer] die Verwendung einer Funktion zum Ändern der Ausgabe der Serialisierung.

Beide Serialisierer akzeptieren optionale Argumente, einschließlich:

  • return_type gibt den Rückgabetyp für die Funktion an. Wenn es weggelassen wird, wird es aus der Typanmerkung abgeleitet.
  • when_used gibt an, wann dieser Serializer verwendet werden soll. Akzeptiert eine Zeichenfolge mit den Werten „always“, „unless-none“, „json“ und „json-unless-none“. Der Standardwert ist „immer“.

PlainSerializer verwendet eine einfache Funktion, um die Ausgabe der Serialisierung zu ändern.

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 empfängt die Roheingaben zusammen mit einer Handlerfunktion, die die Standard-Serialisierungslogik anwendet und den resultierenden Wert ändern kann, bevor er ihn als endgültige Ausgabe der Serialisierung zurückgibt.

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

Überschreiben des Rückgabetyps beim Speichern eines Modells

Während der Rückgabewert von .model_dump() normalerweise als dict[str, Any] beschrieben werden kann, können Sie durch die Verwendung von @model_serializer tatsächlich bewirken, dass ein Wert zurückgegeben wird, der nicht mit dieser Signatur übereinstimmt:

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

Wenn Sie dies tun und dennoch eine ordnungsgemäße Typprüfung für diese Methode erhalten möchten, können Sie .model_dump() in einem if TYPE_CHECKING: -Block überschreiben:

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

Dieser Trick wird in RootModel tatsächlich genau zu diesem Zweck verwendet.

Unterklassen serialisieren

Unterklassen von Standardtypen

Unterklassen von Standardtypen werden automatisch wie ihre Oberklassen ausgegeben:

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

Unterklasseninstanzen für Felder von BaseModel , Datenklassen und TypedDict

Bei der Verwendung von Feldern, deren Annotationen selbst strukturähnliche Typen sind (z. B. BaseModel Unterklassen, Datenklassen usw.), besteht das Standardverhalten darin, den Attributwert so zu serialisieren, als wäre er eine Instanz des annotierten Typs, auch wenn es sich um eine Unterklasse handelt. Genauer gesagt werden nur die Felder des mit Anmerkungen versehenen Typs in das ausgegebene Objekt aufgenommen:

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

!!! Warnung „Migrationswarnung“ Dieses Verhalten unterscheidet sich von der Funktionsweise in Pydantic V1, wo wir beim rekursiven Dumpen von Modellen in Diktate immer alle (Unterklassen-)Felder einbeziehen würden. Der Grund für diese Verhaltensänderung besteht darin, dass Sie genau wissen, welche Felder bei der Serialisierung einbezogen werden könnten, auch wenn bei der Instanziierung des Objekts Unterklassen übergeben werden. Dies kann insbesondere dazu beitragen, Überraschungen zu vermeiden, wenn vertrauliche Informationen wie Geheimnisse als Felder von Unterklassen hinzugefügt werden.

Serialisieren mit Duck-Typing 🦆

!!! Frage „Was ist Serialisierung mit Duck-Typing?“

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.

Wenn Sie ein Duck-Typing-Serialisierungsverhalten im v1-Stil wünschen, können Sie eine Laufzeiteinstellung verwenden oder einzelne Typen mit Anmerkungen versehen.

  • Feld-/Typebene: Verwenden Sie die Annotation SerializeAsAny
  • Laufzeitebene: Verwenden Sie das Flag serialize_as_any , wenn Sie model_dump() oder model_dump_json() aufrufen.

Im Folgenden gehen wir näher auf diese Optionen ein:

SerializeAsAny Anmerkung:

Wenn Sie ein Duck-Typing-Serialisierungsverhalten wünschen, können Sie dies mithilfe der SerializeAsAny -Annotation für einen Typ erreichen:

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

Wenn ein Feld als SerializeAsAny[<SomeType>] annotiert ist, ist das Validierungsverhalten dasselbe, als ob es als <SomeType> annotiert wäre, und Typprüfer wie mypy behandeln das Attribut ebenfalls so, als hätte es den entsprechenden Typ. Bei der Serialisierung wird das Feld jedoch so serialisiert, als ob der Typhinweis für das Feld Any wäre, woher auch der Name stammt.

serialize_as_any Laufzeiteinstellung

Die Laufzeiteinstellung serialize_as_any kann zum Serialisieren von Modelldaten mit oder ohne Duck-Typ-Serialisierungsverhalten verwendet werden. serialize_as_any kann als Schlüsselwortargument an die Methoden model_dump() und model_dump_json von BaseModel s und RootModel s übergeben werden. Es kann auch als Schlüsselwortargument an die Methoden dump_python() und dump_json() von TypeAdapter s übergeben werden.

Wenn serialize_as_any auf True gesetzt ist, wird das Modell mit dem Duck-Typ-Serialisierungsverhalten serialisiert, was bedeutet, dass das Modell das Schema ignoriert und stattdessen das Objekt selbst fragt, wie es serialisiert werden soll. Dies bedeutet insbesondere, dass bei der Serialisierung von Modellunterklassen Felder einbezogen werden, die in der Unterklasse, aber nicht im ursprünglichen Schema vorhanden sind.

Wenn serialize_as_any auf False gesetzt ist (was die Standardeinstellung ist), wird das Modell mithilfe des Schemas serialisiert, was bedeutet, dass Felder, die in einer Unterklasse, aber nicht im ursprünglichen Schema vorhanden sind, ignoriert werden.

!!! Frage „Warum ist diese Flagge nützlich?“ Manchmal möchten Sie sicherstellen, dass unabhängig davon, welche Felder in Unterklassen hinzugefügt wurden, das serialisierte Objekt nur über die Felder verfügt, die in der ursprünglichen Typdefinition aufgeführt sind. Dies kann nützlich sein, wenn Sie in einer Unterklasse so etwas wie ein Feld password: str hinzufügen, das Sie nicht versehentlich in die serialisierte Ausgabe einschließen möchten.

Zum Beispiel:

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. Wenn serialize_as_any auf True gesetzt ist, entspricht das Ergebnis dem von V1.
  2. Wenn serialize_as_any auf False (V2-Standard) gesetzt ist, werden Felder, die in der Unterklasse, aber nicht in der Basisklasse vorhanden sind, nicht in die Serialisierung einbezogen.

Diese Einstellung wird auch bei verschachtelten und rekursiven Mustern wirksam. Zum Beispiel:

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. Sogar verschachtelte User werden mit Feldern gesichert, die nur für User gelten.
  2. Sogar verschachtelte User werden ohne Felder ausgegeben, die nur für User gelten.

!!! Hinweis Das Verhalten des Laufzeitflags serialize_as_any ist fast dasselbe wie das Verhalten der Annotation SerializeAsAny . Es gibt ein paar nuancierte Unterschiede, an deren Lösung wir arbeiten, aber im Großen und Ganzen können Sie von beiden das gleiche Verhalten erwarten. Erfahren Sie mehr über die Unterschiede in dieser aktiven Ausgabe

Überschreiben des Standardwerts serialize_as_any (False)

Sie können die Standardeinstellung für serialize_as_any überschreiben, indem Sie eine Unterklasse von BaseModel konfigurieren, die die Standardeinstellung für das Argument serialize_as_any in model_dump() und model_dump_json() überschreibt, und diese dann als Basisklasse (anstelle von pydantic.BaseModel ) für jedes von Ihnen verwendete Modell verwenden Ich möchte dieses Standardverhalten haben.

Sie könnten beispielsweise Folgendes tun, wenn Sie standardmäßig die Duck-Typing-Serialisierung verwenden möchten:

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. Standardmäßig verwendet model_dump_json das Duck-Typing-Serialisierungsverhalten, was bedeutet, dass das password in der Ausgabe enthalten ist.

pickle.dumps(model)

Pydantic-Modelle unterstützen das effiziente Beizen und Entbeizen.

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

Erweitertes Einschließen und Ausschließen

Die Methoden model_dump und model_dump_json unterstützen include und exclude , die entweder Mengen oder Wörterbücher sein können. Dies ermöglicht eine verschachtelte Auswahl der zu exportierenden Felder:

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

Der True gibt an, dass wir einen gesamten Schlüssel ausschließen oder einschließen möchten, so als ob wir ihn in einen Satz aufnehmen würden. Dies kann auf jeder Tiefenebene erfolgen.

Beim Einschließen oder Ausschließen von Feldern aus einer Liste oder einem Tupel von Untermodellen oder Wörterbüchern ist besondere Vorsicht geboten. In diesem Szenario erwarten model_dump und verwandte Methoden ganzzahlige Schlüssel für den elementweisen Einschluss oder Ausschluss. Um ein Feld von jedem Mitglied einer Liste oder eines Tupels auszuschließen, kann der Wörterbuchschlüssel '__all__' verwendet werden, wie hier gezeigt:

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

Das Gleiche gilt für die Methode model_dump_json .

Ein- und Ausschluss auf Modell- und Feldebene

Zusätzlich zu den expliziten Argumenten exclude und include , die an die Methoden model_dump und model_dump_json übergeben werden, können wir auch die Argumente exclude: bool direkt an den Field Konstruktor übergeben:

Das Festlegen von exclude auf dem Feldkonstruktor ( Field(..., exclude=True) ) hat Vorrang vor „ exclude / include auf model_dump und 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 von der Ausgabe ausgeschlossen, da er in Field ausgeschlossen wurde.

Allerdings hat das Festlegen von exclude im Feldkonstruktor ( Field(..., exclude=True) ) keine Priorität gegenüber den Parametern exclude_unset , exclude_none und exclude_default in model_dump und 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 wurde von der Ausgabe ausgeschlossen, da exclude_none auf True gesetzt wurde und age den Wert None hat.
  2. age wurde von der Ausgabe ausgeschlossen, da exclude_unset auf True gesetzt war und age im Person-Konstruktor nicht festgelegt wurde.
  3. age wird von der Ausgabe ausgeschlossen, da exclude_defaults auf True gesetzt wurde und age den Standardwert None annimmt.

Serialisierungskontext

Sie können ein Kontextobjekt an die Serialisierungsmethoden übergeben, auf das über das info Argument an dekorierte Serialisierungsfunktionen zugegriffen werden kann. Dies ist nützlich, wenn Sie das Serialisierungsverhalten während der Laufzeit dynamisch aktualisieren müssen. Wenn Sie beispielsweise möchten, dass ein Feld abhängig von einem dynamisch steuerbaren Satz zulässiger Werte ausgegeben wird, können Sie dies durch die Übergabe der zulässigen Werte nach Kontext erreichen:

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

Ebenso können Sie einen Kontext zur Validierung verwenden .

model_copy(...)

??? API „API-Dokumentation“ pydantic.main.BaseModel.model_copy

model_copy() ermöglicht das Duplizieren von Modellen (mit optionalen Aktualisierungen), was besonders nützlich ist, wenn mit eingefrorenen Modellen gearbeitet wird.

Beispiel:

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`

本文总阅读量