사용자는 function based view뿐만 아니라 class-based view를 사용하여 API를 작성할 수도 있다. 그럼으로써, class-based view공통의 기능을 재사용하도록 허용해주는 강력한 패턴과, 사용자가 DRY(Do not repeat yourself)하지 않도록 도와주는 것을 확인할 것이다.

Class-based view를 사용하여 API 다시 작성하기


views.py를 열어 기존의 view를 class-based view로 리팩토링 해보자.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class SnippetList(APIView):
    """
    List all snippets, or create a new snippet.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(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)

아직까지는 이전의 케이스와 비슷하게 보인다. 하지만 HTTP 메소드 간에 훨씬 나은 구별이 가능해졌다. views.py의 instance view 또한 업데이트 해주어야 한다.

class SnippetDetail(APIView):
    """
    Retrieve, update or delete a snippet instance.
    """
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, 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, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

아직까지도 function based view와 많이 다르지는 않다. snippets/urls.py를 열어 class-based view에 맞게 url을 리팩토링 하자.

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path('snippets/', views.SnippetList.as_view()),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

여기서 runserver를 돌려 모든 것이 정삭적으로 돌아간다면 다음 단계로 넘어가보자.

Mixins 사용하기


class-based view를 사용함으로써 얻는 큰 이점 중 하나가 쉽게 재사용 가능한 코드의 행동을 작성할 수 있다는 것이다.

지금까지 사용했던 create/retrieve/update/delete 방식과 매우 비슷한 방법들을 방금 생성한 model-backed API에서도 사용하게 될 것이다. 이런 공통의 행동들이 REST Framework의 mixin classs안에서 시행된다.

mixin 클래스를 사용하여 views.py를 작성해보자.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)