自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

Django Rest FrameworkのViewの書き方【APIViewと汎用APIView、ModelViewSetの違い】

thumbnail

Django Rest Framework (以下、DRF)のビューの書き方をまとめる。

下記記事で紹介した素のDjangoのビューと同様、DRFでも継承するビューによって大きく書き方が全く違う。

【関連記事】 : 【Django】ビュー関数とビュークラスの違い、一覧と使い方

本記事では、状況に応じて、適切なビューがコーディングできるようビューの書き方をまとめる。

前提

モデル

from django.db import models

class Topic(models.Model):

    comment     = models.CharField(verbose_name="コメント",max_length=2000)

シリアライザ

from rest_framework import serializers
from .models import Topic

class TopicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Topic
        fields = ("id","comment")

主なビューの書き方

主なビューの書き方は3つ

  • rest_framework.views.APIView を継承する書き方
  • rest_framework.generics.CreateAPIView など汎用APIViewを継承する書き方
  • rest_framework.viewsets.ModelViewSet などを継承する書き方

rest_framework.views.APIView は django.views.View と等価とみて良いだろう。

rest_framework.generics.CreateAPIView など汎用APIViewは、 素のDjangoのListViewやDetailViewに相当する。

rest_framework.viewsets.ModelViewSet などは、ListViewやDetailView、CreateViewなどをひとまとめにしたViewと考えると理解が早い。

rest_framework.views.APIView を継承する書き方

もっとも詳細な書き方ができるのは、APIViewを継承する書き方。

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import TopicSerializer
from .models import Topic

class TopicView(APIView):
    def get(self, request, *args, **kwargs):
        queryset    = Topic.objects.all()

        if "pk" in kwargs:
            topic       = get_object_or_404(queryset, pk=kwargs["pk"])
            serializer  = TopicSerializer(topic)
        else:
            serializer  = TopicSerializer(queryset, many=True)

        return Response(serializer.data)

    def post(self, request, *args, **kwargs):
        serializer  = TopicSerializer(data=request.data)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


    def put(self, request, pk=None, *args, **kwargs):
        topic       = Topic.objects.get(pk=pk)
        serializer  = TopicSerializer(topic, data=request.data)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk=None, *args, **kwargs):
        topic       = Topic.objects.get(pk=pk)
        topic.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

しかし、これではRestfulの設計思想からは離れてしまう。

普段からViewを継承したビュークラスを使っている場合、馴染みやすいが、保守の観点ではあまりよろしくはない。

ルーティング

通常のDjangoのViewと同様、.as_view() を実行した返り値を呼び出す。

from django.contrib import admin
from django.urls import path,include

from bbs import views

urlpatterns = [ 
    path('admin/', admin.site.urls),
    path('api/topics/', views.TopicView.as_view()),
    path('api/topics/<pk>/', views.TopicView.as_view()),
]

rest_framework.generics.CreateAPIView など汎用APIViewを継承する書き方

from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import TopicSerializer

class TopicCreateView(CreateAPIView):
    serializer_class = TopicSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

作成の機能だけが必要な場合は、CreateAPIViewを使うことで簡単に実現できる。

だが、後に機能追加が想定される場合、最初からCreateAPIViewを使うと、コードが煩雑になる。

この汎用APIViewは素のDjangoの汎用Viewと同様、リファクタリング用に使うべきと思われる。

ルーティング

通常のDjangoのViewと同様、.as_view() を実行した返り値を呼び出す。

from django.contrib import admin
from django.urls import path,include

from bbs import views

urlpatterns = [ 
    path('admin/', admin.site.urls),
    path('api/topics/', views.TopicCreateView.as_view()),
]

rest_framework.viewsets.ModelViewSet などを継承する書き方

from django.shortcuts import render
from rest_framework import viewsets
from .serializers import TopicSerializer
from .models import Topic

class TopicView(viewsets.ModelViewSet):
    serializer_class    = TopicSerializer
    queryset            = Topic.objects.all()

とてもシンプルに書くことができる。

これだけで、CRUDが実現される。

Restfulの設計思想通り、メソッドごとにシンプルな機能(CRUD)を提供している。

ルーティング

ModelViewSetの場合、APIViewと違ってルーティングの書き方も異なる

from django.contrib import admin
from django.urls import path,include


from rest_framework import routers
from bbs import views

router = routers.DefaultRouter()
router.register(r"topics", views.TopicView, "topic")

urlpatterns = [ 
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
]

この1行のURL設定で

  • GET api/topics/: トピック一覧を取得するためのエンドポイント
  • POST api/topics/: 新しいトピックを作成するためのエンドポイント
  • GET api/topics/{id}/: 特定のトピックの詳細情報を取得するためのエンドポイント
  • PUT api/topics/{id}/: 特定のトピックを更新するためのエンドポイント
  • PATCH api/topics/{id}/: 特定のトピックの一部を更新するためのエンドポイント
  • DELETE api/topics/{id}/: 特定のトピックを削除するためのエンドポイント

この6個分のURL設定に対応している。

laravelの –resource のような書き方だ。laravelの–resourceも機能を最小限に絞ることで、Restfulなコントローラを作ることができる。

結論

【Restful】Django+Reactビギナーが40分で掲示板アプリ(SPA)を作る方法【axios】ではModelViewSetを使ってビューを作った。

このModelViewSetはモデルを元に作ったシリアライザを指定するだけでCRUDを実現してくれる。

Restfulの設計思想に従うのであれば、このModelViewSetを使う。

より詳細な機能を実装させたい場合は、views.APIView を使う。ただし、Restfulの設計思想とは離れてしまうため、保守が難しくなるだろう。

基本は、ModelViewSetを使い、複雑な機能が必要になった場合は、その都度APIViewを使うほうが無難と思われる。

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ