[DRF] 심화 개념

minhi-!·2023년 2월 14일
0

Django REST Framework

목록 보기
2/3

DRF의 다양한 뷰

클라이언트 측에서 우리 API로 요청을 보낼 때에는 URL과 Method를 조합하여 요청을 보내는데, 이때 같은 URL 내의 여러 Method를 처리하는 방법으로 함수형과 클래스형이 구분되었다.

그렇다면 URL과 Method의 조합은 몇 가지 정도 있을까? 일반적인 구조로는 다음의 5개의 조합이 존재한다.

DRF로 API를 개발할 때 만들어야 하는 기능은 GET(list) POST(create) GET(retrieve) PUT(update) DELETE(destroy)의 5가지가 전부이다. 이들은 모델 API 만들기에서 공통적으로 쓰이는 기능이므로 개발자들은 이를 최대한 편하고 쉽게 만들고자 하였고, 그럼 지금부터 그 방법에 대해 알아보자.


DRF mixins

class BooksAPI(APIView):                    
    def get(self, request):                 
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
    def post(self, request):
        serializer = BookSerializer(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)

class BookAPI(APIView):
    def get(self, request, bid):
        book = get_object_or_404(Book, bid=bid)
        serializer = BookSerializer(book)
        return Response(serializer.data, status=status.HTTP_200_OK)

앞서 작성했던 CBV를 보면 books, book과 같은 모델로부터 가져온 데이터나 BookSerializer와 같은 시리얼라이저가 여러 번 사용되고 있음을 확인할 수 있다. 이 부분의 중복을 제거하기 위해 DRF에서는 mixins라는 것을 미리 정의하고 있다.

mixins는 APIView에서 request의 method마다 시리얼라이저 코드를 작성하는 것을 줄이기 위해 클래스 레벨에서 시리얼라이저를 등록하고 있다. 각 method에서는 시리얼라이저 코드를 작성하는 대신 각 method별 mixin에 연결하여 사용하기만 하면 된다.


views.py

from rest_framework import generics                     # generics 불러오기
from rest_framework import mixins                       # mixins 불러오기
from .models import Book
from .serializers import BookSerializer

class BooksAPIMixins(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):                                  # ListModelMixin, CreateModelMixin 상속 + GenericAPIView는 공통적으로 상속
    queryset = Book.objects.all()
    serializer = BookSerializer

    def get(self, request, *args, **kwargs):            # GET - 전체 도서 조회
        return self.list(request, *args, **kwargs)      # ListModelMixin과 연결
    
    def post(self, request, *args, **kwargs):           # POST - 도서 1권 등록
        return self.create(request, *args, **kwargs)    # CreateModelMixin과 연결

class BookAPIMixins(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):     # RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin 상속 + GenericAPIView는 공통적으로 상속
    queryset = Book.objects.all()
    serializer = BookSerializer()
    lookup_field = 'bid'                                # Django 기본 모델 pk가 아닌 bid를 pk로 사용하고 있으니 lookup_field로 설정한다.

    def get(self, request, *args, **kwargs):            # 도서 1권 조회
        return self.retrieve(request, *args, **kwargs)  # RetrieveModelMixin과 연결

    # 도서 1권 수정, 도서 1권 삭제 기능 추가!
    def put(self, request, *args, **kwargs):            # 도서 1권 수정
        return self.update(request, *args, **kwargs)    # UpdateModelMixin과 연결
    def delete(self, request, *args, **kwargs):         # 도서 1권 삭제
        return self.destroy(request, *args, **kwargs)   # DestroyModelMixin과 연결


example/urls.py

from django.urls import path, include
from .views import helloAPI, BooksAPIMixins, BookAPIMixins

urlpatterns = [
	path("hello/", helloAPI),
    path("mixin/books/", BooksAPIMixins.as_view()),
    path("mixins/book/<int:bid>/", BookAPIMixins.as_view()),
]

CBV와 동일하게 .as_view()도 작성해야 한다.


DRF generics

mixins보다 간단하게 REST API의 5가지 메인 기능을 수행할 수 있다.

DRF genericsmixins를 조합해 일종의 mixins 세트를 미리 만들어둔다.


views.py

from rest_framework import generics                     # generics 불러오기
from .models import Book
from .serializers import BookSerializer

class BooksAPIGenerics(generics.ListCreateAPIView):             # 전체 도서 조회 + 도서 1권 등록
    queryset = Book.objects.all()
    serializer_class = BookSerializer

class BookAPIGenerics(generics.RetrieveUpdateDestroyAPIView):   # 도서 1권 조회 + 도서 1권 수정 + 도서 1권 삭제
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    lookup_field = 'bid'


example/urls.py

from django.urls import path, include
from .views import helloAPI, BooksAPIMixins, BookAPIMixins

urlpatterns = [
	path("hello/", helloAPI),
    path("mixin/books/", BooksAPIMixins.as_view()),
    path("mixins/book/<int:bid>/", BookAPIMixins.as_view()),
]

CBV와 동일하게 .as_view()도 작성해야 한다.



🥸 내가 이해한 대로-

APIMixins

  • 정의할 때 아래에서 사용할 기능을 수행하는 mixin들을 선택적으로 상속받는다.
  • generics.GenericAPIView는 공통적으로 상속받는다.
  • get() post() 함수 내용은 작성할 필요없이 그냥 return self.기능() 하면 된다.

APIGenerics

  • 정의할 때 아래에서 사용할 기능을 수행하는 generic 하나를 상속받는다.
  • generics.GenericAPIView는 상속받지 않는다.
  • get() post() 함수 자체를 정의하지도 않고 따라서 return 구문도 작성하지 않는다. 여러 개의 mixins를 사용하던 방식에서는 각 mixins를 메소드에 매칭시키는 과정이 필요했고 그것이 return 구문에 있었다면, 이제 mixins가 조합된 generics를 사용하다 보니 어차피 List+Create면 GET과 POST가 들어간다는 것을 이미 알고 있는 것이 되기 때문이다.


DRF Viewset & Router

여태까지 작업했던 것은 하나의 클래스가 하나의 URL을 담당하는 방식이었다. URL마다 클래스를 만들었고 각 클래스에서 해당 URL로 들어오는 다양한 메소드를 처리할 수 있도록 하였다.

그러다 보니 queryset이나 serializer_class 부분이 겹치게 되었다. 하나의 클래스로 하나의 모델을 전부 처리해 줄 수 있으면 겹치는 부분이 없어질 것이다. Viewset에서는 그러한 문제를 해결할 수 있다.

Viewset내부적으로 5가지 기능의 mixins를 상속받아 정의된 ModelViewSet을 이용해 querysetserializer_class를 정의해 주는 것으로 모델에 대한 기본적인 REST API를 만들 수 있다.


views.py

from rest_framework import viewsets

class BookViewSet(viewsets.ModelViewSet):
	queryset = Book.objects.all()
    serializer_class = BookSerializer


example/urls.py

from rest_framework import routers
from .views import BookViewSet

router = routers.SimpleRouter()				# 라우터 객체 선언
router.register('books', BookViewSet)		# 라우터에 ViewSet 등록

urlpatterns = router.urls

urlpatterns에는 기존처럼 각 path를 일일이 등록하는 것이 아니라, router.urls를 include하여 마치 프로젝트의 urls.py에서 앱의 url을 불러오는 것처럼 작성하였다.


Viewset & Router 장점

  • 하나의 클래스로 하나의 모델에 대한 내용을 전부 작성할 수 있으며, 그에 따라 queryset이나 serializer_class 등 겹치는 부분을 최소화할 수 있다.
  • 라우터를 통해 URL을 일일이 지정하지 않아도 일정한 규칙의 URL을 만들 수 있다.
profile
개발 공부 기록용

0개의 댓글