Django REST Framework で ModelViewSet を使いこなす

2024-04-02

Django REST Framework の ModelViewSet の詳細解説

概要

  • 開発時間の短縮: 個々のビューを記述する必要がなくなり、モデルを公開するまでの時間を大幅に短縮できます。
  • コードの簡潔化: コードの冗長性を減らし、コードベースをより読みやすく、保守しやすいものにします。
  • 一貫性のあるインターフェース: すべてのモデルに対して統一されたインターフェースを提供し、API の使いやすさを向上させます。
  • 強力な機能: フィルタリング、ソート、パジネーション、シリアル化など、さまざまな機能を備えています。

ModelViewSet を使用するには、以下の手順が必要です。

  1. モデル: API エンドポイントを公開したいモデルを定義します。
  2. シリアライザー: モデルデータを JSON などのフォーマットに変換するためのシリアライザークラスを作成します。
  3. ViewSet: ModelViewSet クラスを継承し、必要なオプションを設定します。
  4. ルーター: URL と ViewSet をマッピングするためのルーターを設定します。

ModelViewSet は、以下の属性を使用して動作をカスタマイズできます。

  • queryset: エンドポイントで使用するクエリセットを指定します。
  • serializer_class: 使用するシリアライザークラスを指定します。
  • filter_backends: フィルタリングバックエンドのリストを指定します。
  • ordering_fields: ソート可能なフィールドのリストを指定します。
  • pagination_class: パジネーションクラスを指定します。

ModelViewSet の例

以下は、Product モデルに対して CRUD 操作を提供する ModelViewSet の例です。

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=6, decimal_places=2)

from django.contrib.auth.models import User

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('id', 'name', 'price')

from rest_framework.viewsets import ModelViewSet

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

このコードは、以下の URL エンドポイントを生成します。

  • /products/: すべての製品を取得
  • /products/<pk>/: 特定の製品を取得
  • /products/: 新しい製品を作成
  • /products/<pk>/: 特定の製品を更新
  • /products/<pk>/: 特定の製品を削除


ModelViewSet のサンプルコード

フィルタリングとソート

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    category = models.CharField(max_length=255)

from django.contrib.auth.models import User

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('id', 'name', 'price', 'category')

from rest_framework import filters

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    ordering_fields = ['name', 'price', 'category']
  • 製品名、価格、カテゴリーでフィルタリング
  • 製品名、価格、カテゴリーでソート

パジネーション

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    category = models.CharField(max_length=255)

from django.contrib.auth.models import User

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('id', 'name', 'price', 'category')

from rest_framework.pagination import PageNumberPagination

class ProductPagination(PageNumberPagination):
    page_size = 10

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    pagination_class = ProductPagination

このコードは、以下の機能を追加します。

  • ページネーションによる結果の分割

カスタムアクション

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    category = models.CharField(max_length=255)

from django.contrib.auth.models import User

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('id', 'name', 'price', 'category')

from rest_framework.decorators import action
from rest_framework.response import Response

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    @action(detail=False, methods=['get'])
    def stats(self, request):
        products = Product.objects.all()
        return Response({
            'total_products': products.count(),
            'average_price': products.aggregate(models.Avg('price'))['price__avg']
        })

このコードは、以下の機能を追加します。

  • /products/stats/ エンドポイントへのカスタムアクション
  • すべての製品の統計情報を提供

関連モデル

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    category = models.CharField(max_length=255)

class Category(models.Model):
    name = models.CharField(max_length=255)

from django.contrib.auth.models import User

class ProductSerializer(serializers.ModelSerializer):
    category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all())

    class Meta:
        model = Product
        fields = ('id', 'name', 'price', 'category')

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

このコードは、以下の機能を追加します。

  • シリアライザーで関連モデル (Category) をシリアル化

認証とパーミッション

from django.contrib.auth.models import User

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    category = models.CharField(max_length=2


ModelViewSet を使用しないその他の方法

個別ビュー

ModelViewSet を使用せずに、個々のビューを作成して CRUD 操作を定義することができます。これは、単純なモデルや特殊な要件がある場合に役立ちます。

from django.views.generic import DetailView, CreateView, UpdateView, DeleteView

from .models import Product

class ProductDetailView(DetailView):
    model = Product

class ProductCreateView(CreateView):
    model = Product

class ProductUpdateView(UpdateView):
    model = Product

class ProductDeleteView(DeleteView):
    model = Product

ジェネリックビュー

Django REST Framework は、汎用ビューを提供しており、CRUD 操作を自動的に生成することができます。これは、ModelViewSet と似ていますが、より柔軟性があります。

from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView

from .models import Product

class ProductListAPIView(ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

class ProductCreateAPIView(CreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

class ProductRetrieveAPIView(RetrieveAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

class ProductUpdateAPIView(UpdateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

class ProductDestroyAPIView(DestroyAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

カスタムビュー

上記の方法でニーズが満たされない場合は、カスタムビューを作成することができます。これは、高度な要件がある場合に必要です。

from rest_framework.views import APIView

from .models import Product

class ProductCustomView(APIView):
    # ...

ModelViewSet は、Django REST Framework でモデルを公開する最も一般的な方法ですが、他にもいくつかの方法があります。ニーズに最適な方法を選択する必要があります。




エンドポイントでのUnsupportedMediaType例外の扱い

UnsupportedMediaType例外は、クライアントが送信したリクエストのメディアタイプが、APIエンドポイントでサポートされていない場合に発生します。これは、クライアントが誤ったContent-Typeヘッダーを送信したか、またはDRFが認識できない新しいメディアタイプを送信しようとした場合に発生します。




Django REST Framework:MethodNotAllowed例外をカスタマイズする方法

Django REST Frameworkは、Django上でREST APIを構築するための強力なツールです。しかし、API開発においては、クライアントからのリクエストが許可されていないメソッドを使用するなど、さまざまなエラーが発生する可能性があります。


エンドポイントでのUnsupportedMediaType例外の扱い

UnsupportedMediaType例外は、クライアントが送信したリクエストのメディアタイプが、APIエンドポイントでサポートされていない場合に発生します。これは、クライアントが誤ったContent-Typeヘッダーを送信したか、またはDRFが認識できない新しいメディアタイプを送信しようとした場合に発生します。