🚀

pydantic v2のserializeが便利なので紹介します

2023/07/15に公開

pydanticがv2がリリースされた

pydanticのv2がリリースされました。

今回は私がv2になってちょっと嬉しかった機能を紹介したいと思います。

dict変換時に別処理をはさみたかった

pydantic v1はBaseModelを使ったクラスを作成した際、
dict()で辞書変換を行うことが出来ました。

from datetime import datetime

from pydantic import BaseModel
from zoneinfo import ZoneInfo


class UserInfo(BaseModel):
    name: str
    age: int
    dt: datetime


dt = datetime.now(tz=ZoneInfo("Asia/Tokyo"))

user_info = UserInfo(name="hogehoge", age=20, dt=dt)

print(user_info.dict())
# dtは現在時間が表示される
# {'name': 'hogehoge', 'age': 20, 'dt': datetime.datetime(2023, 7, 15, 7, 9, 49, 735299, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))}

UserInfodtというdatetime型を所持しています。
このdtをdict変換時にdatetime -> str型への変換を行いたいパターンがしばしばありました。

その場合、dtに対して何かしらの処理を途中にはさみたいのですが
今までのpydanticでは途中に処理を入れる事が出来ませんでした。
そのため、私は以下のように対応していました。

from datetime import datetime
from typing import Any, Dict

from pydantic import BaseModel
from zoneinfo import ZoneInfo


class UserInfo(BaseModel):
    name: str
    age: int
    dt: datetime

    def to_dict(self) -> Dict[str, Any]:
        """dtをstr変換して返却

        Returns:
            Dict[str, Any]: 辞書型変換後のデータ
        """
        parameter = self.dict()
        parameter.update({"dt": self.dt.isoformat()})
        return parameter


dt = datetime.now(tz=ZoneInfo("Asia/Tokyo"))

user_info = UserInfo(name="hogehoge", age=20, dt=dt)

print(user_info.to_dict())
# {'name': 'hogehoge', 'age': 20, 'dt': '2023-07-15T07:18:47.517362+09:00'}

自前でto_dict()を準備し、その中で変換を行う処理です。
私の中では許容範囲でしたが、もっといい方法はないかな…と思っていました。

v2で進化していた

v2ではどうなったかといいますと、だいぶ進化していました。
まず、辞書変換時に使用するのはdict()ではなくmodel_dump()です。

from datetime import datetime

from pydantic import BaseModel
from zoneinfo import ZoneInfo


class UserInfo(BaseModel):
    name: str
    age: int
    dt: datetime


dt = datetime.now(tz=ZoneInfo("Asia/Tokyo"))

user_info = UserInfo(name="hogehoge", age=20, dt=dt)

print(user_info.model_dump())
# {'name': 'hogehoge', 'age': 20, 'dt': datetime.datetime(2023, 7, 15, 7, 21, 3, 704288, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))}

しれっと追加されていたfield_serializer

dict()model_dump()に変わっていただけなら特に変化はありません。
何か変わっているところあるかなと思っていたところ、見つけました。
field_seralizer
https://docs.pydantic.dev/2.0/usage/serialization/

説明をみるに、辞書化するときに任意の処理を挟める事ができるようです。
先程、独自メソッドを使っていましたが
field_seralizerを使えば、以下のように書くことができます。

from datetime import datetime

from pydantic import BaseModel, field_serializer
from zoneinfo import ZoneInfo


class UserInfo(BaseModel):
    name: str
    age: int
    dt: datetime

    @field_serializer("dt")
    def serialize_dt(self, dt: datetime) -> str:
        return dt.isoformat()


dt = datetime.now(tz=ZoneInfo("Asia/Tokyo"))

user_info = UserInfo(name="hogehoge", age=20, dt=dt)

print(user_info.model_dump())
# {'name': 'hogehoge', 'age': 20, 'dt': '2023-07-15T09:47:44.789119+09:00'}

field_serializerをデコレータにし、対象となるフィールドを指定します。
後は関数内でカスタマイズして返却します。
返却値がフィールドキーの値として最終的に出力されます。
(例でいうところのdt:{日付の文字列}

model_serializer

model_serializerを使えば、クラス自体の返却値を任意に変更できます。

from datetime import datetime

from pydantic import BaseModel, model_serializer
from zoneinfo import ZoneInfo


class UserInfo(BaseModel):
    name: str
    age: int
    dt: datetime

    @model_serializer
    def serialize_dt(self) -> dict[str, str]:
        return {"now_time": self.dt.isoformat()}


dt = datetime.now(tz=ZoneInfo("Asia/Tokyo"))

user_info = UserInfo(name="hogehoge", age=20, dt=dt)

print(user_info.model_dump())
# {'now_time': '2023-07-15T09:55:08.056624+09:00'}

たまーに、キー・バリューを逆転して格納したい時があったのですが
これを使えばスマートに記述出来そうです。

pydanticで開発体験を向上させよう

pydanticは型を厳密に検証してくれるため
まるで静的型付け言語のような安心感がある開発を体感することができます。

pydanticはFastAPIと組み合わせて使われる事が多いですが
単体でも有効に使うことができます。

みなさんも是非試してみてください!

参考

Discussion