Перейти к содержанию

Сериализация

Помимо прямого доступа к атрибутам модели через имена их полей (например, model.foobar ), модели можно конвертировать, выгружать, сериализовать и экспортировать несколькими способами.

!!! Совет «Сериализация и дамп» Pydantic использует термины «сериализация» и «дамп» как синонимы. Оба относятся к процессу преобразования модели в словарь или строку в формате 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 "Документация по API" pydantic.main.BaseModel.model_dump

Это основной способ преобразования модели в словарь. Подмодели будут рекурсивно преобразованы в словари.

!!! Примечание. Единственным исключением из подмоделей, преобразуемых в словари, является то, что RootModel и его подклассы будут иметь дамп значения root поля напрямую, без словаря-обертки. Это также делается рекурсивно.

!!! note Примечание. Вычисляемые поля можно использовать для включения данных property и cached_property в выходные данные model.model_dump(...) .

Пример:

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

Метод .model_dump_json() сериализует модель непосредственно в строку в кодировке JSON, которая эквивалентна результату, полученному с помощью .model_dump() .

См. аргументы для получения дополнительной информации.

!!! Примечание. Pydantic может сериализовать в JSON многие часто используемые типы, которые в противном случае были бы несовместимы с простым json.dumps(foobar) (например, datetime , date или 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) и итерация

Модели Pydantic также можно преобразовать в словари с помощью dict(model) , а также можно перебирать поля модели с помощью for field_name, field_value in model: . При таком подходе возвращаются необработанные значения полей, поэтому подмодели не будут преобразованы в словари.

Пример:

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

Также обратите внимание, что RootModel преобразуется в словарь с помощью ключа 'root' .

Пользовательские сериализаторы

Pydantic предоставляет несколько функциональных сериализаторов для настройки сериализации модели в словарь или JSON.

Сериализацию можно настроить для поля с помощью декоратора @field_serializer и для модели с помощью декоратора @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"}

!!! Примечание. Один сериализатор также можно вызвать для всех полей, передав специальное значение '*' декоратору @field_serializer.

Кроме того, PlainSerializer и WrapSerializer позволяют использовать функцию для изменения вывода сериализации.

Оба сериализатора принимают необязательные аргументы, включая:

  • return_type указывает тип возвращаемого значения для функции. Если он опущен, он будет выведен из аннотации типа.
  • when_used указывает, когда следует использовать этот сериализатор. Принимает строку со значениями «всегда», «если-нет», «json» и «json-если-нет». По умолчанию «всегда».

PlainSerializer использует простую функцию для изменения вывода сериализации.

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 получает необработанные входные данные вместе с функцией-обработчиком, которая применяет стандартную логику сериализации и может изменять результирующее значение перед возвратом его в качестве окончательного результата сериализации.

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

Переопределение типа возвращаемого значения при дампе модели

Хотя возвращаемое значение .model_dump() обычно можно описать как dict[str, Any] , с помощью @model_serializer вы действительно можете заставить его возвращать значение, которое не соответствует этой сигнатуре:

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

Если вы хотите сделать это и при этом получить правильную проверку типов для этого метода, вы можете переопределить .model_dump() в блоке 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: ...

Этот трюк на самом деле используется в RootModel именно для этой цели.

Сериализация подклассов

Подклассы стандартных типов

Подклассы стандартных типов автоматически сбрасываются, как и их суперклассы:

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

Экземпляры подклассов для полей BaseModel , классов данных, TypedDict

При использовании полей, аннотации которых сами являются структуроподобными типами (например, подклассы BaseModel , классы данных и т. д.), поведением по умолчанию является сериализация значения атрибута, как если бы оно было экземпляром аннотированного типа, даже если это подкласс. Точнее, в дамп объекта будут включены только поля аннотированного типа:

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

!!! предупреждение «Предупреждение о миграции» Такое поведение отличается от того, как все работало в Pydantic V1, где мы всегда включали все поля (подкласса) при рекурсивном выгрузке моделей в словари. Мотивация этого изменения в поведении заключается в том, что оно помогает гарантировать, что вы точно знаете, какие поля могут быть включены при сериализации, даже если подклассы передаются при создании экземпляра объекта. В частности, это может помочь предотвратить неожиданности при добавлении конфиденциальной информации, такой как секреты, в поля подклассов.

Сериализация с помощью утиного набора 🦆

!!! вопрос «Что такое сериализация с утиной типизацией?»

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.

Если вам нужно поведение сериализации с утиной типизацией в стиле v1, вы можете использовать настройку времени выполнения или аннотировать отдельные типы.

  • Уровень поля/типа: используйте аннотацию SerializeAsAny
  • Уровень времени выполнения: используйте флаг serialize_as_any при вызове model_dump() или model_dump_json()

Подробнее об этих вариантах мы поговорим ниже:

Аннотация SerializeAsAny :

Если вам нужно поведение сериализации с утиным типом, это можно сделать с помощью аннотации SerializeAsAny для типа:

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

Когда поле помечено как SerializeAsAny[<SomeType>] , поведение проверки будет таким же, как если бы оно было помечено как <SomeType> , а средства проверки типов, такие как mypy, также будут рассматривать атрибут как имеющий соответствующий тип. Но при сериализации поле будет сериализовано так, как если бы подсказка типа для поля была Any , отсюда и название.

параметр serialize_as_any времени выполнения

Параметр среды выполнения serialize_as_any можно использовать для сериализации данных модели с поведением сериализации типа «утка» или без него. serialize_as_any можно передать в качестве аргумента ключевого слова в методы model_dump() и model_dump_json классов BaseModel и RootModel . Его также можно передать в качестве аргумента ключевого слова методам dump_python() и dump_json() TypeAdapter s.

Если для serialize_as_any установлено значение True , модель будет сериализована с использованием поведения сериализации утиного типа. Это означает, что модель будет игнорировать схему и вместо этого запрашивать сам объект, как его следует сериализовать. В частности, это означает, что при сериализации подклассов модели будут включены поля, присутствующие в подклассе, но не в исходной схеме.

Если для serialize_as_any установлено значение False (по умолчанию), модель будет сериализована с использованием схемы, а это означает, что поля, присутствующие в подклассе, но не в исходной схеме, будут игнорироваться.

!!! вопрос «Почему этот флаг полезен?» Иногда необходимо убедиться, что какие бы поля ни были добавлены в подклассы, сериализованный объект будет содержать только поля, перечисленные в исходном определении типа. Это может быть полезно, если вы добавите что-то вроде password: str в подкласс, который вы не хотите случайно включать в сериализованный вывод.

Например:

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. Если для serialize_as_any установлено значение True , результат соответствует результату V1.
  2. Если для serialize_as_any установлено значение False (по умолчанию в V2), поля, присутствующие в подклассе, но не в базовом классе, не включаются в сериализацию.

Этот параметр действует даже для вложенных и рекурсивных шаблонов. Например:

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. Даже вложенные экземпляры модели User сохраняются с полями, уникальными для подклассов User .
  2. Даже вложенные экземпляры модели User выгружаются без полей, уникальных для подклассов User .

!!! note Примечание. Поведение флага времени выполнения serialize_as_any почти такое же, как и поведение аннотации SerializeAsAny . Есть несколько нюансов, над которыми мы работаем, но по большей части вы можете ожидать одинакового поведения от обоих. Подробнее о различиях читайте в этом активном выпуске.

Переопределение значения по умолчанию serialize_as_any (False)

Вы можете переопределить настройку по умолчанию для serialize_as_any , настроив подкласс BaseModel , который переопределяет значение по умолчанию для serialize_as_any для model_dump() и model_dump_json() , а затем использовать его в качестве базового класса (вместо pydantic.BaseModel ) для любой вашей модели. хочу иметь это поведение по умолчанию.

Например, вы можете сделать следующее, если хотите использовать сериализацию с утиной типизацией по умолчанию:

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 будет использовать поведение сериализации утиного типа, что означает, что поле password включается в выходные данные.

pickle.dumps(model)

Модели Pydantic поддерживают эффективное травление и расмаринование.

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

Расширенное включение и исключение

Методы model_dump и model_dump_json поддерживают аргументы include и exclude , которые могут быть наборами или словарями. Это позволяет вложенный выбор полей для экспорта:

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 указывает, что мы хотим исключить или включить весь ключ, как если бы мы включили его в набор. Это можно сделать на любом уровне глубины.

Особую осторожность необходимо соблюдать при включении или исключении полей из списка или кортежа подмоделей или словарей. В этом сценарии model_dump и связанные с ним методы ожидают целочисленные ключи для поэлементного включения или исключения. Чтобы исключить поле из каждого члена списка или кортежа, можно использовать словарный ключ '__all__' , как показано здесь:

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

То же самое относится и к методу model_dump_json .

Включение и исключение на уровне модели и поля

В дополнение к явным аргументам exclude и include передаваемым в методы model_dump и model_dump_json , мы также можем передать аргументы exclude: bool непосредственно в конструктор Field :

Установка exclude в конструкторе поля ( Field(..., exclude=True) ) имеет приоритет над exclude / include в model_dump и 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 исключено из вывода, поскольку оно исключено в Field .

При этом установка exclude в конструкторе поля ( Field(..., exclude=True) ) не имеет приоритета над параметрами exclude_unset , exclude_none и exclude_default в model_dump и 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 исключен из выходных данных, поскольку для exclude_none установлено значение True , а ageNone .
  2. age исключен из выходных данных, поскольку для exclude_unset установлено значение True , а age не был установлен в конструкторе Person.
  3. age исключен из выходных данных, поскольку для exclude_defaults установлено значение True , а age принимает значение по умолчанию None .

Контекст сериализации

Вы можете передать объект контекста методам сериализации, доступ к которому можно получить из аргумента info для декорированных функций сериализатора. Это полезно, когда вам нужно динамически обновлять поведение сериализации во время выполнения. Например, если вы хотите, чтобы поле выгружалось в зависимости от динамически управляемого набора разрешенных значений, это можно сделать, передав разрешенные значения по контексту:

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

Аналогичным образом вы можете использовать контекст для проверки .

model_copy(...)

??? API "Документация по API" pydantic.main.BaseModel.model_copy

model_copy() позволяет дублировать модели (с дополнительными обновлениями), что особенно полезно при работе с замороженными моделями.

Пример:

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`

本文总阅读量