DRF3 튜토리얼 3 - 클래스 기반 뷰
원문 - Class Based Views
번역을 허락해 준 Tom Christie에게 고마움을 전합니다.
튜토리얼 3: 클래스 기반 뷰
앞서 함수 기반으로 만들었던 API 뷰를 클래스 기반 뷰로도 만들 수 있습니다. 이는 일반적인 기능을 재사용하게 해주며 코드 중복(DRY)도 막아주기 때문에 굉장히 쓸모 있는 패턴입니다.
클래스 기반 뷰로 API 재작성하기
먼저 최상단 뷰를 클래스 기반 뷰로 재작성해봅시다. 이를 위해 views.py
를 약간 리팩터링해야 합니다.
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):
"""
코드 조각을 모두 보여주거나 새 코드 조각을 만듭니다.
"""
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
파일을 열어, 코드 조각 하나를 담당하는 뷰도 수정해봅시다.
class SnippetDetail(APIView):
"""
코드 조각 조회, 업데이트, 삭제
"""
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)
훌륭합니다. 그렇지만 역시나 아직까진 함수 기반 뷰와 크게 다르지 않습니다.
이제 urls.py
도 수정해야 합니다. 클래스 기반 뷰를 사용해야 하니까요.
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
자, 끝났습니다. 개발 서버를 실행해 보면 이전과 동일하게 작동하는 모습을 볼 수 있을 겁니다.
믹스인 사용하기
클래스 기반 뷰를 사용하여 얻는 가장 큰 이점은 기능들을 손쉽게 조합할 수 있다는 점입니다.
지금까지 사용한 생성/조회/업데이트/삭제 등의 명령은 일반적으로 모델을 사용할 때의 뷰와 비슷합니다. 이러한 보편적인 기능을 REST 프레임워크에서는 믹스인 클래스로 구현해두었습니다.
이제 뷰에 믹스인 클래스를 추가하는 모습을 살펴봅시다. 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)
여기서 무슨 일이 벌어지는지 꼼꼼히 따져봅시다. GenericAPIView
와 ListModelMixin
, CreateModelMixin
을 사용하여 뷰를 만들었습니다.
기본 뷰(GenericAPIView
)는 핵심 기능을 제공하며, 믹스인 클래스들은 .list()
나 .create()
기능을 제공합니다. 여기서는 이 기능들을 get
과 post
메서드에 적절히 연결하였습니다. 이정도면 간단하죠?
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)
비슷하게, 여기서도 GenericAPIView
는 핵심 기능을 제공하며, 나머지 믹스인들이 .retrieve()
, .update()
, .destroy()
기능을 제공합니다.
제네릭 클래스 기반 뷰 사용하기
믹스인 클래스를 사용하여 뷰의 코드를 꽤 많이 줄였지만, 더 줄일 수 있습니다. REST 프레임워크에서는 믹스인과 연결된 제네릭 뷰를 제공합니다. 이를 사용하면 views.py
파일이 굉장히 짧아집니다.
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
보세요, 정말 간결하죠? 별 노력 없이 아주 많은 기능을 구현했는데도 코드는 더 깔끔하고 훨씬 Django다워졌습니다.
튜토리얼 4부에서는 인증과 권한을 API에서 어떻게 다룰지를 살펴보겠습니다.
COMMENTS