💭
# 3.2 Serializer 設計の実装パターン(Django REST Framework)
Serializer 設計の実装パターン(Django REST Framework)
前回の記事では API 設計の総論を紹介した。
今回はその中から特に重要な Serializer 設計 を掘り下げる。
Serializer は Django REST Framework(DRF)の中心的な仕組みであり、
- 入力のバリデーション
- モデルとの変換
- レスポンスの整形
を担う。
実務では「とりあえず ModelSerializer
」では済まない場面が多いため、いくつかのパターンを押さえておくと便利。
1. シンプルに ModelSerializer を使う
最も基本的なパターン。小規模アプリや管理系 API ではこれで十分なことも多い。
# serializers.py
from rest_framework import serializers
from .models import Order
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = "__all__"
これだけで CRUD API の入出力を網羅できる。
2. 一覧用と詳細用を分ける
一覧 API では軽量な項目だけを返し、詳細 API では関連情報も含めるようにする。
class OrderListSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["id", "customer_name", "total_amount"]
class OrderDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["id", "customer_name", "total_amount", "created_at", "items"]
View 側で切り替えて使う:
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
def get_serializer_class(self):
if self.action == "list":
return OrderListSerializer
return OrderDetailSerializer
3. 入力用と出力用を分ける
入力時には受け付けないフィールド(例: id, created_at)を除外したいケース。
class OrderInputSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["customer_name", "total_amount"]
class OrderOutputSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["id", "customer_name", "total_amount", "created_at"]
View で使い分ける:
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
def get_serializer_class(self):
if self.action in ["create", "update", "partial_update"]:
return OrderInputSerializer
return OrderOutputSerializer
4. ネストされた Serializer
関連モデルを展開して返すパターン。
(例: 注文と注文アイテム)
class OrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ["id", "product_name", "quantity", "price"]
class OrderDetailSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = ["id", "customer_name", "total_amount", "items"]
レスポンス例:
{
"id": 1,
"customer_name": "株式会社テスト",
"total_amount": 50000,
"items": [
{ "id": 1, "product_name": "商品A", "quantity": 2, "price": 10000 },
{ "id": 2, "product_name": "商品B", "quantity": 1, "price": 30000 }
]
}
5. カスタムフィールド
モデルに存在しない値を返したい場合は SerializerMethodField
を使う。
class OrderListSerializer(serializers.ModelSerializer):
total_amount_display = serializers.SerializerMethodField()
class Meta:
model = Order
fields = ["id", "customer_name", "total_amount", "total_amount_display"]
def get_total_amount_display(self, obj):
return f"¥{obj.total_amount:,}"
6. バリデーションの追加
モデルだけでは表現できない制約を Serializer で補う。
class OrderInputSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["customer_name", "total_amount"]
def validate_total_amount(self, value):
if value <= 0:
raise serializers.ValidationError("金額は 0 より大きい必要があります。")
return value
まとめ
Serializer 設計の実装パターンを整理すると次の通り。
- シンプルに
ModelSerializer
を使う - 一覧用 / 詳細用に分ける
- 入力用 / 出力用に分ける
- ネスト構造を展開する
- カスタムフィールドで柔軟に拡張
- 独自のバリデーションを追加
これらを組み合わせることで、「フロントで扱いやすく、かつ安全な API」 を提供できる。
次回はこの流れをさらに発展させて、エラーハンドリングの実装パターン を紹介する予定。
Discussion