REST APIレスポンスJSONからPythonクラスへの変換
メタデータ、ページネーション、ネストされたデータを含む完全なREST APIレスポンスをPython dataclassまたはPydanticモデルの階層に変換する方法を学びます。
Advanced Patterns
詳細な説明
PythonでのAPI完全レスポンスのモデリング
本番環境のAPIレスポンスには、メタデータ、ページネーション、エラーエンベロープ、ネストされたリソースが含まれます。これらをPython型に変換するには、体系的なアプローチが必要です。
JSONの例
{
"status": "ok",
"data": {
"users": [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
]
},
"pagination": {
"page": 1,
"per_page": 20,
"total": 152,
"total_pages": 8
}
}
生成されるPython(dataclass)
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
email: str
@dataclass
class UserData:
users: list[User]
@dataclass
class Pagination:
page: int
per_page: int
total: int
total_pages: int
@dataclass
class ApiResponse:
status: str
data: UserData
pagination: Pagination
生成されるPython(Pydantic)
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str
class UserData(BaseModel):
users: list[User]
class Pagination(BaseModel):
page: int
per_page: int
total: int
total_pages: int
class ApiResponse(BaseModel):
status: str
data: UserData
pagination: Pagination
ジェネリックレスポンスラッパー
すべてのエンドポイントで同じエンベロープを使用するAPIの場合:
from typing import Generic, TypeVar
T = TypeVar("T")
@dataclass
class ApiResponse(Generic[T]):
status: str
data: T
pagination: Pagination
# 使用法
response: ApiResponse[UserData] = ...
エラー処理
APIはエラー時に異なる形状を返します:
from typing import Optional
@dataclass
class ApiError:
code: str
message: str
@dataclass
class ApiResponse(Generic[T]):
status: str
data: Optional[T] = None
pagination: Optional[Pagination] = None
error: Optional[ApiError] = None
daciteによるデシリアライゼーション
from dacite import from_dict
response = from_dict(data_class=ApiResponse, data=json.loads(raw_json))
for user in response.data.users:
print(user.name)
ベストプラクティス
- リーフ型を先に定義 —
User、次にUserData、最後にApiResponse。 - 再利用可能な型を抽出 —
Paginationはエンドポイント間で共有される。 - ラッパーにジェネリクスを使用 — 各リソースのエンベロープの重複を避ける。
ユースケース
REST APIのPython SDKを構築しており、すべてのエンドポイントの成功レスポンス、エラーレスポンス、ページネーションメタデータをモデル化する完全な型階層が必要な場合に使用します。