コンテンツにスキップ

Serialization

フィールド名(例えばmodel.foobar)から直接モデル属性にアクセスする以外にも、モデルはさまざまな方法で変換、ダンプ、シリアライズ、エクスポートすることができます。

Serialize versus dump

Pydanticでは、"serialize"と"dump"という用語を同じ意味で使用しています。どちらも、モデルを辞書またはJSONでエンコードされた文字列に変換するプロセスを指します。

Pydantic以外では、"シリアライズ"という言葉は通常、メモリ内のデータを文字列やバイトに変換することを意味します。 しかし、Pydanticのコンテキストでは、オブジェクトをPydanticモデルやデータクラスなどのより構造化された形式から、dictなどのPython組み込みで構成されたより構造化されていない形式に変換することには、非常に密接な関係があります。

プリミティブへの変換時には"dump"、文字列への変換時には"serialize"という単語を使用することで、これらのシナリオを区別することができます(場合によっては区別します)が、実際には、必ずしも文字列やバイトへの変換を意味するわけではありませんが、"serialize"という単語を両方の状況を指すためによく使用します。

model.model_dump(...)

API Documentation

pydantic.main.BaseModel.model_dump

これは、モデルを辞書に変換する主要な方法です。サブモデルは再帰的に辞書に変換されます。

Note

辞書に変換されるサブモデルの1つの例外は、RootModelとそのサブクラスが、ラッピング辞書なしで直接ダンプされるrootフィールド値を持つことです。これも再帰的に行われます。

Note

computed fieldsを使用して、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 Documentation

pydantic.main.BaseModel.model_dump_json

.model_dump_json()メソッドは、.model_dump()で生成された結果と等価なJSONエンコードされた文字列にモデルを直接シリアライズします。

詳細については、argumentsを参照してください。

Note

Pydanticは、一般的に使用されている多くの型を、単純なjson.dumps(foobar)(例えばdatetimedateUUID)と互換性のないJSONにシリアライズすることができます。

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) and iteration

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_does_が'root'キーを持つ辞書に変換されることにも注意してください。

Custom serializers

Pydanticには、モデルを辞書やJSONにシリアライズする方法をカスタマイズするためのfunctional serializersがいくつか用意されています。

シリアライゼーションは、@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"}

Note

@field_serializerデコレータに特別な値'*'を渡すことで、すべてのフィールドで単一のシリアライザを呼び出すこともできます。

さらに、PlainSerializerWrapSerializerでは、シリアライゼーションの出力を変更する関数を使うことができます。

どちらのシリアライザも、以下のようなオプションの引数を受け付けます。

  • return_typeは、関数の戻り値の型を指定します。省略すると、型の注釈から推測されます。
  • when_usedは、このシリアライザをいつ使用すべきかを指定します。'always'、'unless-none'、'json'、および'json-unless-none'の値を持つ文字列を受け入れます。デフォルトは'always'です。

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

Overriding the return type when dumping a model

.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

これを行いながら、このメソッドに対して適切な型チェックを行いたい場合は、if TYPE_CHECKING:ブロック内の.model_dump()をオーバーライドすることができます。

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でまさにこの目的のために使われています。

Serializing subclasses

Subclasses of standard types

標準タイプのサブクラスは、スーパークラスと同様に自動的にダンプされます。

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

Subclass instances for fields of BaseModel, dataclasses, TypedDict

構造体に似た型(例えばBaseModelのサブクラスやデータクラスなど)のアノテーションを持つフィールドを使用する場合、デフォルトの動作では、サブクラスであっても、属性値をアノテーション付き型のインスタンスであるかのようにシリアライズします。具体的には、_annotated_typeのフィールドのみがダンプされたオブジェクトに含まれます。

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

Migration Warning

この動作は、モデルをディクテーションに再帰的にダンプするときに常にすべての(サブクラス)フィールドを含めるPydantic V1での動作とは異なります。 この動作変更の背後にあるモチベーションは、オブジェクトのインスタンス化時にサブクラスが渡された場合でも、シリアライズ時にどのフィールドが含まれるかを正確に把握できるようにするためです。 特に、これは、サブクラスのフィールドとして秘密などの機密情報を追加するときに、不測の事態を防ぐのに役立ちます。

Serializing with duck-typing 🦆

What is serialization with duck typing?

ダック型シリアライゼーションとは、オブジェクトのスキーマに存在するフィールドではなく、オブジェクト自体に存在するフィールドに基づいてオブジェクトをシリアライズする動作のことです。 つまり、オブジェクトがシリアライズされると、サブクラスに存在するが元のスキーマには存在しないフィールドが、シリアライズされた出力に含まれます。

この動作はPydantic V1ではデフォルトでしたが、V2では、オブジェクトのインスタンス化時にサブクラスが渡された場合でも、シリアライズ時にどのフィールドが含まれるかを正確に把握できるように変更されました。 これにより、たとえば機密情報を含むサブクラスをシリアライズする際のセキュリティリスクを防ぐことができます。

v1スタイルのduck-typingシリアライゼーション動作が必要な場合は、ランタイム設定を使用するか、個々の型にアノテーションを付けることができます。

  • フィールド/型レベル:SerializeAsAnyアノテーションを使用します。
  • 実行時レベル:model_dump()またはmodel_dump_json()を呼び出すときにserialize_as_anyフラグを使用してください。

これらのオプションについては、以下で詳しく説明します。

SerializeAsAny annotation:

型指定のシリアライズ動作を省略したい場合は、型に対して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 runtime setting

serialize_as_anyランタイム設定は、duck型のシリアライゼーション動作の有無にかかわらず、モデルデータをシリアライズするために使用できます。

serialize_as_anyは、BaseModelおよびRootModelmodel_dump()およびmodel_dump_jsonメソッドにキーワード引数として渡すことができます。また、TypeAdapterdump_python()およびdump_json()メソッドにキーワード引数として渡すこともできます。

serialize_as_anyTrueに設定されている場合、モデルはduck型の直列化動作を使用して直列化されます。つまり、モデルはスキーマを無視し、代わりにオブジェクト自体にどのように直列化されるべきかを尋ねます。

特に、これは、モデルサブクラスがシリアライズされるとき、サブクラスに存在するが元のスキーマには存在しないフィールドが含まれることを意味します。

serialize_as_anyFalse(デフォルト)に設定されている場合、モデルはスキーマを使用してシリアライズされます。つまり、サブクラスに存在するが元のスキーマには存在しないフィールドは無視されます。

Why is this flag useful?

場合によっては、サブクラスに追加されたフィールドが何であっても、直列化されたオブジェクトには元の型定義にリストされたフィールドしかないことを確認したいことがあります。 これは、シリアル化された出力に誤って含めたくない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_anyTrueに設定されている場合、結果はV1の結果と一致します。
  2. serialize_as_anyFalse(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アノテーションの動作とほとんど同じです。 私たちが解決しようとしている微妙な違いがいくつかありますが、ほとんどの場合、両方から同じ動作が期待できます。 このの違いについてはactive issueを参照してください。

Overriding the serialize_as_any default (False)

serialize_as_anyのデフォルト設定を上書きするには、model_dump()model_dump_json()serialize_as_any引数のデフォルトを上書きするBaseModelのサブクラスを設定し、このデフォルトの動作をさせたいモデルの基本クラスとして(pydantic.BaseModelの代わりに)それを使用します。

たとえば、デフォルトでduck-typingシリアライゼーションを使用する場合は、次のようにします。

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はduck-typingシリアライゼーション動作を使用します。つまり、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

Advanced include and exclude

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メソッドにも当てはまります。

Model- and field-level include and exclude

model_dumpおよびmodel_dump_jsonメソッドに渡される明示的な引数excludeおよびincludeに加えて、exclude:bool引数をFieldコンストラクタに直接渡すこともできます。

フィールドコンストラクタ(Field(... ,exclude=True))にexcludeを設定すると、model_dumpmodel_dump_jsonexclude/includeよりも優先されます。

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. valueFieldで除外されているため、出力から除外されます。

とはいうものの、フィールドコンストラクタ(Field(...,exclude=True))にexcludeを設定しても、model_dumpmodel_dump_jsonexclude_unsetexclude_noneexclude_defaultパラメータよりも優先されません。

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. exclude_noneTrueに設定され、ageNoneであるため、ageは出力から除外されます。
  2. exclude_unsetTrueに設定され、ageがPersonコンストラクタで設定されなかったため、ageは出力から除外されました。
  3. exclude_defaultsTrueに設定されているため、ageは出力から除外され、ageはデフォルト値のNoneを取ります。

Serialization Context

デコレートされたシリアライザ関数への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'}

同様に、use a context for validationすることもできます。

model_copy(...)

API Documentation

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`