DEV/🟢 DJANGO

[Django REST framework] 튜토리얼 3 : 클래스 기반 View

개발을 하게 되. 2022. 2. 26. 01:00

Class-based View

클래스 기반 보기를 사용하여 API 보기를 작성할 수 있다. 

보시다시피 이것은 일반적인 기능을 재사용하고 코드를 DRY 하게 유지하는 데 도움이 되는 강력한 패턴이다. 

여기서 DRY 란 Don't repeat yourself 로 반복하지 않는 코드패턴을 말한다.  

클래스기반 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

"""
APIView를 상속받는 클래스 SnippetList 뷰 
"""
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)

 

def get( ) , def post 를 보듯 다른 HTTP method를 더 잘 구분하였다. 

또한 view.py 에서 instance view ( Detail 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)

좋아보이긴 하지만...아직까진 함수기반의 뷰와 비슷해보인다. 

우선 넘어가고 url.py로 클래스 기반 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)

Mixins을 사용하자

클래스 기반 뷰를 쓰는 가장 큰 이유는 재사용이 쉽게 가능하다는 것이다. 

상속이라는 개념을 통해 코드를 반복해서 사용하는 것을 줄일 수 있다. 

지금까지 사용한 Create/ Retrieve/ Update/ Delete 명령은 일반적으로 모델을 사용할때의 뷰와 비슷하다. 

이러한 일반적인 동작은 REST framework에서 Mixin 클래스로 구현되어 있다. 

Mixin 클래스를 사용해서 View를 구성해보자.

 

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

from rest_framework import mixins # mixins 를 import하자
from rest_framework import generics # generics도 import하자.


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)

위에서 정확히 어떤일이 일어나는지 살펴보자. mixin과 generic 을 위주로 보자. 

GenericAPIView를 사용하여 뷰를 작성하고, 

ListModeMixin 과 CreateModeMixin을 추가했다. 

GernericAPIView는 핵심기능을 제공하고 

Mixin 클래스는 .list().create() 기능을 제공한다. 

 

그런 다음 이 기능들을 get과 post 메소드에 명시적으로 연결하면 된다. 

함수 get 은 자신의 list 기능과 연결.

함수 post는 자신의 create기능과 연결 . 

 

디테일뷰도 똑같이 수행할 수 있다. 

class SnippetDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

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

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

그렇다면 genric class based views는 왜 사용할까? 

Mixin 클래스 를 사용해서 이전의 코드를 줄일 수 있었지만, 더욱 줄일 수 있다.

REST framework는 이미 mixin과 Generic View를 결합으로 줄일 수 있다. 

 

Generic view가 무엇인지 깊게 알고싶다면 밑을 참고하자. 

https://www.django-rest-framework.org/api-guide/generic-views/

 

Generic views - Django REST framework

 

www.django-rest-framework.org

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

"""
SnippetList는 generic 안에 있는 ListCreateAPIView를 상속받음. 
"""
class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

와우..코드가 많이 간결해졌다. 

look like good, clean, idiomatic(그나라에 맞는) Django로 만들었다. 

 

 

 

다음시간은  part 4 of the tutorial,

authentication and permissions for our API에 대해 알아볼 것이다.