💭

# 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