コンテンツにスキップ

シリアライズ

フィールド名 (例: 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

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

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

!!! note計算フィールドを使用して、 model.model_dump(...)出力にpropertycached_propertyデータを含めることができます。

例:

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()メソッドは、 .model_dump()によって生成された結果と同等の JSON エンコード文字列にモデルを直接シリアル化します。

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

!!! Pydantic は、単純なjson.dumps(foobar) (例: datetimedate 、またはUUID ) と互換性のない、一般的に使用される多くの型を 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)と反復

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 にシリアル化する方法をカスタマイズするために、いくつかの [関数型シリアライザー][pydantic.functionerializers] を提供します。

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

シリアル化は、フィールドでは [@field_serializer][pydantic.function_serializers.field_serializer] デコレーターを使用してカスタマイズでき、モデルでは [@model_serializer][pydantic.function_serializers.model_serializer] デコレーターを使用してカスタマイズできます。

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

from pydantic import BaseModel, ConfigDict, field_serializer, model_serializer


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

    dt: datetime
    diff: timedelta

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


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


class Model(BaseModel):
    x: str

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


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

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

さらに、[PlainSerializer][pydantic.functionerializers.PlainSerializer] と [WrapSerializer][pydantic.function_serializers.WrapSerializer] を使用すると、関数を使用してシリアル化の出力を変更できます。

どちらのシリアライザーも、次のようなオプションの引数を受け入れます。

  • return_type関数の戻り値の型を指定します。省略した場合は、型アノテーションから推測されます。
  • when_usedこのシリアライザーをいつ使用するかを指定します。 「always」、「unless-none」、「json」、および「json-unless-none」の値を持つ文字列を受け入れます。デフォルトは「常に」です。

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

これを行いながら、このメソッドの適切な型チェックを取得したい場合は、 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で使用されています。

サブクラスのシリアル化

標準型のサブクラス

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

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

!!!警告 "移行警告" この動作は、モデルを dict に再帰的にダンプするときに常にすべての (サブクラス) フィールドを含める 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注釈を使用します
  • ランタイム レベル: model_dump()またはmodel_dump_json()を呼び出すときに、 serialize_as_anyフラグを使用します。

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

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 、キーワード引数としてBaseModelおよびRootModelmodel_dump()およびmodel_dump_jsonメソッドに渡すことができます。 TypeAdapterdump_python()メソッドとdump_json()メソッドにキーワード引数として渡すこともできます。

serialize_as_anyTrueに設定されている場合、モデルはアヒル型のシリアル化動作を使用してシリアル化されます。これは、モデルがスキーマを無視し、代わりにオブジェクト自体にどのようにシリアル化するかを尋ねることを意味します。これは特に、モデルのサブクラスがシリアル化されると、サブクラスには存在するが元のスキーマには存在しないフィールドが含まれることを意味します。

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

!!!質問「なぜこのフラグが役立つのですか?」場合によっては、サブクラスにどのようなフィールドが追加されたとしても、シリアル化されたオブジェクトには元の型定義にリストされているフィールドのみが含まれるようにしたいことがあります。これは、シリアル化された出力に誤って含めたくないサブクラスに、 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のデフォルト設定をオーバーライドするには、 model_dump()およびmodel_dump_json()へのserialize_as_any引数のデフォルトをオーバーライドするBaseModelのサブクラスを構成し、それを任意のモデルの基本クラス ( 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_dumpmodel_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_dumpmodel_dump_jsonメソッドに渡される明示的な引数excludeincludeに加えて、 exclude: bool引数をFieldコンストラクターに直接渡すこともできます。

フィールド コンストラクターのexclude設定 ( Field(..., exclude=True) ) はmodel_dumpおよびmodel_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. value Fieldで除外されているため、出力から除外されます。

ただし、フィールド コンストラクターでのexclude設定 ( Field(..., exclude=True) ) は、 model_dumpおよびmodel_dump_jsonexclude_unsetexclude_none 、およびexclude_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デフォルト値のNone使用するため、 age出力から除外されます。

シリアル化コンテキスト

コンテキスト オブジェクトをシリアル化メソッドに渡すことができます。シリアル化メソッドには、 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`

本文总阅读量