3 - Class based views - Django REST framework
We can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code DRY. We'll start by rewriting the root view as a cla
www.django-rest-framework.org
함수형이 익수해졌기 때문에, 굳이 class형으로 view를 짜야할까라는 의구심이 가득했었다. 그러나 해당 튜토리얼을 보면서 그런 생각은 다 사라졌고, class형 view를 열심히 공부해둬야겠다는 생각밖에 없었다. 일단 공식문서에서 설명하기로는 재사용성이 증가한다고 한다. 그리고 한번 쓱 훓어본 결과, 장고에서 지원하는 제너릭뷰, apiView 상속, 믹스인이라는 강력한 기능일 지원했기 때문이다. 공부하면서 드는 생각인데 나름 뛰어난 개발자들은 어디서 뭘 보고 공부하는 것일까.. 공식문서 보고 공부하는 걸까..? 그런 정형화 된 틀을 알면 진짜 공부하기 편할텐데. 독학의 슬픔인가 ㅠ 그래도 걱정마라! 장고공식문서에 따르면 클래스형 뷰 작성이 항상 우수하다고 말할 수 있는것은 아니라고 나온다 !
여튼 이번에는 클래스를 기반으로 View를 작성하는 방법에 대해서 알아보도록 하겠다!
# 클래스 기반 View 작성하기
## SnippetList
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)
코드는 당연히 공식문서의 코드를 참고했다. 가장 크게 달라진점은, View자체를 def(함수)가 아닌 class(클래스)로 만들었다는 점과 (APIView)를 상속받았다는 것 이다. 그 외 코드는 똑같다. 클래스 안에서 get과 post로 HTTP-method를 나눴는데 함수형 view에서는 직접 request.method == "GET" 으로 나눈거에 비해 간결해졌다는 것을 알 수 있다.
## SnippetDetail
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)
여기도 똑같다. 아직까지 코드의 구성은 크게 달라진게 없고, 어디선가 들어오는 pk를 매개변수로 받아 각 게시글의 고유 id값 즉 primary-key로 사용한다.
## class형 view의 urls작성법
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)
urls.py를 약간 수정해줘야 한다. 원래는 views.SnippetList 이렇게 사용했었지만 뒤에 .as_view()를 추가해줬다. 그거 말고는 다른게 없다.
# APIView ?
Views - Django REST framework
www.django-rest-framework.org
view를 class로 작성하면서 APIView를 상속 받게 했다. APIView가 그래서 뭘까 궁금해질 수 밖에 없다. 그래서 찾아 본걸 정리해보려고 한다.
class SnippetList(APIView):
def get(self, request, format=None):
return
def post(self, request, format=None):
return
def update(self, request, format=None) :
return
def delete(self, requset, format=None) :
return
- Request는 Django의 HttpRequest 인스턴스가 아닌 rest-framework의 request의 인스턴스를 사용한다
- 장고의 HttpResponse가 아닌 rest-framework의 Response를 반환한다.
- APIException 예외케이스가 발견되면 적절하게 response를 변환해 사용할 수 있게 한다.
- 들어오는 request를 authenicate하고, 적절한 권한 혹은 throttle(제한사항)을 체크하여 실행한다.
라고 하는데 몇몇은 정확히 와 닿지가 않는다 사용해가면서 알아갈 거 같다. 장고 공식문서에서도 DRF의 믹스인이나 제너릭뷰 뷰셋등이 처음에는 복잡할 수 밖에 없다고 한다. 그래서 공식문서외에 추가적인 문서를 추천해주는데 잊지 않게 하기 위해 기록해 두겠다.
Django REST Framework 3.12 -- Classy DRF
What is this? Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs. It provides class based generic API views and serializers. We've taken all the attributes and methods that every view/serializer defines or inherit
www.cdrf.co
아 그리고 추가적으로 CBV(클래스 기반 뷰)의 장점은 Detail에서 get_object()함수를 만들어 클래스내에서 재사용 한거와 같이, 재사용성이 용이하다는 것이다!
# 믹스인 사용, Mixins
API를 작성하면 생성/ 검색 / 업데이트 / 삭제는 항상 구성하는 요소일 것이고 그 형태또한 비슷할 것으로 예상된다. 그래서 DRF에서는 이를 미리 정의 시켜 놓고 편리하게 사용 할 수 있도록 해주는 기능인거 같다.
특정한 클래스에 상속을 통해 새로운 속성이나 새로운 기능을 추가 하는 것을 의미한다. <- Mix + in
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)
간단한 설명을 붙이겠다. mixins.ListModelMixin, mixins.CreateModelMixin을 사용했다. 그리고 queryset = Snippet.objects.all()과 serializer_class = SnippetSerializer를 지정해줬는데, 사실 이건 왜 이렇게 했는지는 정확히 모르겠지만, class기반을 사용할때 저렇게 꼭 정해진 변수(이름)으로 뭔가를 지정해주고, 그것을 기준점으로 하여 get, post가 쓰이는걸로 유추 중이다. 확실히 알게 된다면 다시 돌아와서 글을 수정하도록 하겠다. 여튼 그렇게 하고나면 def get(....)에서 self.list를 쓸 수 있게 된다. 순전히 mixins.List..를 사용했기 때문에 저렇게 쉽게 list를 뽑아와서 response해준다. post또한 똑같다. 이렇게 비슷한 원리로 아래와 같이, put과 delete도 구현할 수 있게 된다. 일단, 정말 코드가 간결해진걸 알 수 있다.
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)
난 사실 이게 작동할까라는 굉장한 의구심을 가지고 있었는데.. 당연한거지만 잘 작동한다. 일단 여기서 궁금한건 현업에서 개발할때는 이렇게 절대 안쓰겠지..? 커스텀하겠지...?
이렇게 믹스인의 막강한 기능을 확인해 볼 수 있었다.
# 제너릭 클래스 뷰 generic class-based views
사실 이거 보고 충격을 금치 않았다.. 난 장고 공부하면서 나름 게시판 CRUD구현 했던거에 엄청 뿌듯했었다.
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] 제네릭 뷰(Generic View) 살펴보기
장고의 제네릭 뷰를 살펴보고 예제를 작성해 봅니다.
velog.io
제너릭뷰에 대해서 잘 설명하신 분이 계신다. 일단 이번 포스팅은 여기서 끝내고, 다음에 추가적으로 정리를 하던가 다음 튜툐리얼을 진행해 보도록 하겠다!