始めに
Pythonにてアプリケーション内ではdatetime
として扱いつつ、APIとしてはYYYY-mm-dd
等の特定のフォーマットの文字列で返却したいことがあります。
今回はPydantic
を用いて実装する方法を記事にします。
環境
- Python
- 3.12.4
- Pydantic
- 2.8.2
実装
field_serializer を使用する
field_serializer
を用いることで、シリアライズ時にフォーマットしてくれるようになります。
次のコードでは、特に何もしていなければISO8601のフォーマットで返却し、field_serializer
が有効な個所では%Y-%m-%d %H:%M:%S
で返却するテストを記載しています。
from datetime import datetime from zoneinfo import ZoneInfo from pydantic import BaseModel, field_serializer, PlainSerializer import json from typing import Annotated class _TestModel(BaseModel): dt: datetime normal_dt: datetime @field_serializer("dt") def serialize_datetime(self, dt: datetime, _info): return dt.strftime("%Y-%m-%d %H:%M:%S") class TestFieldSerializer: async def test_01(self): dt = datetime(2024, 12, 1, 2, 3, 4, tzinfo=ZoneInfo("UTC")) model = _TestModel(dt=dt, normal_dt=dt) actual_str = model.model_dump_json() actual_dict = json.loads(actual_str) assert actual_dict.get("dt") == "2024-12-01 02:03:04" assert actual_dict.get("normal_dt") == "2024-12-01T02:03:04Z"
PlainSerializer で型を表現する
Annotated
とPlainSerializer
を組み合わせることで型として表現できます。各モデルごとに定義する必要がないので、基本的にはこちらを採用するほうがメリットがあります。
CustomDatetime = Annotated[ datetime, PlainSerializer(lambda dt: dt.strftime("%Y-%m-%d %H:%M:%S")) ] class _TestModel2(BaseModel): dt: CustomDatetime normal_dt: datetime class TestCustomDatetime: async def test_serialize(self): dt = datetime(2024, 12, 1, 2, 3, 4, tzinfo=ZoneInfo("UTC")) model = _TestModel2(dt=dt, normal_dt=dt) actual_str = model.model_dump_json() actual_dict = json.loads(actual_str) assert actual_dict.get("dt") == "2024-12-01 02:03:04" assert actual_dict.get("normal_dt") == "2024-12-01T02:03:04Z" async def test_deserialize(self): # GIVEN dt = datetime(2024, 12, 1, 2, 3, 4, tzinfo=ZoneInfo("UTC")) model = _TestModel2(dt=dt, normal_dt=dt) actual_str = model.model_dump_json() # WHEN actual = _TestModel2.model_validate_json(actual_str) # シリアライズ時にTZが削除されている assert actual.dt == dt.replace(tzinfo=None) assert actual.normal_dt == dt
ソースコード
終わりに
基本的にはフロントでフォーマットすればいいのですが、全処理をバックエンドに押し付ける場合にはこういう処理も可能です。
また、CSV作成処理等でも流用できます。