일하는/Cloud, Web

Django REST Framework : (3) Class-based Views

김논리 2020. 9. 18. 16:09

원문은 Django REST Framework 공식 페이지 🏠 www.django-rest-framework.org/tutorial/3-class-based-views/ 에서 확인할 수 있습니다.

 

ungodly-hour.tistory.com/24

 

Django REST Framework : (2) Requests and Responses

원문은 Django REST Framework 공식 페이지 🏠 https://www.django-rest-framework.org/tutorial/2-requests-and-responses/ 에서 확인할 수 있습니다. https://ungodly-hour.tistory.com/23 Django REST Fram..

ungodly-hour.tistory.com


 

앞서 Tutorial 2에서 우리는 뷰를 아래와 같이 api_view 데코레이터를 이용한 함수 기반으로 작성하였다.

 

@api_view(['GET', 'POST'])
def snippet_list(request):   # 함수 기반의 뷰
    ....
    return Response(serializer.data)

@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    ....
    return Response(serializer.data)

 

DRF에서는 함수 기반의 뷰가 아닌 클래스 기반의 뷰를 사용하여 뷰를 만들 수도 있다.

클래스 기반 뷰는 일반적인 기능을 재사용하여 코드의 중복을 막을 수 있기 때문에, 우리의 코드를 DRY(Don't Repeat Yourself) 하게끔 유지하는데 도움이 되는 강력한 패턴이다!

 

Rewriting our API using class-based views

root 뷰를 클래스 기반으로 다시 작성해 보자. 이를 위해 snippets/view.py를 약간 리팩토링 해야 한다.

 

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView    # 클래스 기반 뷰인 APIView를 사용한다.
from django.http import Http404


class SnippetList(APIView):   # snippet_list 함수를 class로 변경
    """
    List all code snippets, or create a new snippet.
    """
    def get(self, request, format=None):    # GET에 대한 함수를 만든다.
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):   # POST에 대한 함수를 만든다.
        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)

 

이전 코드와 거의 똑같아 보이지만, snippet_list() 함수를 클래스로 변경하고, HTTP 메소드를 서로 다른 함수로 분리하여 더 잘 구분할 수 있게 하였다.

 

마찬가지로 snippet_detail() 함수에 해당하는 클래스도 만들어 보자.

 

class SnippetDetail(APIView):    # snippet_detail 함수를 클래스로 변경
    """
    Retrieve, update or delete a code snippet.
    """
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):    # GET에 대한 함수를 만든다.
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):    # PUT에 대한 함수를 만든다.
        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): # DELETE에 대한 함수를 만든다.
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT

 

snippet_detail() 함수 역시 큰 수정 없이 클래스로 변경할 수 있다.

 

이제 변경된 클래스 기반 뷰를 사용할 수 있도록 snippets/urls.py를 수정해 보자.

 

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
 
urlpatterns = [
    path(r'snippets/', views.SnippetLists.as_view()),
    path(r'snippets/<int:pk>', views.SnippetDetail.as_view()),
]
 
urlpatterns = format_suffix_patterns(urlpatterns)

 

APIView에서 제공하는 as_view() 메소드를 이용하여, 간단하게 url과 연결할 수 있다.

이제 서버를 실행하면, 이전과 동일하게 작동하는 것을 확인할 수 있다.

 

Using mixins

클래스 기반 뷰를 사용하여 얻을 수 있는 가장 큰 장점 중 하나는 재사용 가능한 뷰들을 쉽게 조합할 수 있다는 것이다. 지금까지 우리가 사용한 create/retrieve/update/destroy 명령은 일반적으로 모델을 사용할 때의 뷰와 매우 유사하다. DRF에서는 이러한 일반적인 동작들을 mixin 클래스로 제공하고 있다.

mixin 클래스는 다중 상속이 가능하다. 다시 말해, 다양한 종류의 클래스 기반 뷰를 레고처럼 이리저리 조합하여 사용할 수 있다는 것이다!

 

이제 snippet/views.py 파일을 다음과 같이 수정하여 mixin 클래스를 사용한 뷰를 만들어 보자.

 

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins      # mixins 클래스를 사용한다.
from rest_framework import generics    # 일반적으로 사용되는 뷰의 template을 가져올 수 있다.


class SnippetList(mixins.ListModelMixin,    # list를 위한 mixin
                  mixins.CreateModelMixin,  # create을 위한 mixin
                  generics.GenericAPIView): # 일반 API view
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):  # GET은 list mixin으로 연결
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):  # POST는 create mixin으로 연결
        return self.create(request, *args, **kwargs)

 

get(), post() 메소드가 매우 간단해진 것을 확인할 수 있다.

SnippetList 클래스(뷰)를 GenericAPIView와 ListModelMixin, 그리고 CreateModelMixin을 사용하여 만들었다. GenericAPIView는 뷰의 핵심 기능을 제공하고, mxin 클래스들은 .list().create() 와 같은 기능들을 제공한다. 위 코드에서는 이 기능들을 각각 get()과 post() 메소드에 명시적으로 바인딩하여 사용하고 있다. (아주 간단하죠?)

 

이제 SnippetDetail 클래스도 함께 수정해 보자.

 

class SnippetDetail(mixins.RetrieveModelMixin,  # retrieve를 위한 mixin
                    mixins.UpdateModelMixin,    # update를 위한 mixin
                    mixins.DestroyModelMixin,   # destroy를 위한 mixin
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):     # GET은 retrieve mixin으로 연결
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):     # PUT은 update mixin으로 연결
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):  # DELETE는 destroy mixin으로 연결
        return self.destroy(request, *args, **kwargs)

 

SnippetList 클래스와 마찬가지로 GenericAPIView 클래스를 사용하여 뷰의 핵심 기능을 제공하고, 추가적으로 retrieve(), update() 그리고 destroy() 기능을 제공하였다.

이제 서버를 실행하고, API를 테스트해 보자. 이전과 동일하게 동작하는 것을 확인할 수 있다.

 

Using generic class-based views

mixin 클래스를 사용하여 코드의 양을 많이 줄였지만, 더 줄이는 것도 가능하다. DRFmixin과 연결된 generic view를 제공하는데, 이를 사용하면, 아래와 같이 snippets/views.py를 더 간단하게 줄일 수 있다.

 

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


class SnippetList(generics.ListCreateAPIView):  # list, create을 지원하는 generic 뷰
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView): # retrieve, update, destroy를 지원하는 generic 뷰
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

 

DRF에서 제공하는 다양한 generic view를 이용하여 단 몇줄의 코드로 많은 기능을 구현할 수 있으며, 코드는 더 깔끔하고 Django스러워졌다.

 


ungodly-hour.tistory.com/28

 

Django REST Framework : (4) Authentication & Permissions

원문은 Django REST Framework 공식 페이지 🏠 www.django-rest-framework.org/tutorial/4-authentication-and-permissions/ 에서 확인할 수 있습니다. ungodly-hour.tistory.com/26 Django REST Framework : (..

ungodly-hour.tistory.com