DRF 공부하기 (7) :: GenericAPIView와 Mixins

PHYYOU·2020년 10월 18일
3

출처
출처 2


위의 블로그의 내용을 복붙함.

DRF 7 - GenericAPIView와 Mixins

아래 사례는 Udemy의 "The complete Guide to Django Restframework and Vue JS" 강의 중 Section4 의 : Django REST Framework - Level Two 내용으로서 Ebook API 작성사례이며, DRF 9편까지 이어집니다.

1. 기본 개념

GenericaAPIVeiw Tutorial 보러 가기

  • Django Rest Framework에서는 GenericAPIView에 List, Create 등 다양한 믹스인 클래스를 결합해 APIView를 구현할 수 있음
  • GenericAPIView는 CRUD(생성/읽기/수정/삭제)에서 공통적으로 사용되는 속성을 제공하고, Mixin은 CRUD 중 특정 기능을 수행하는 메소드를 제공함
  • GenericAPIView와 Mixins으로 정확한 기능 구현이 어려울 때, Override해서 customizing할 수 있음

2. Ebook 코드

1) EbookListCreateAPIView

from rest_framework import generics
from rest_framework import mixins

from ebooks.models import Ebook
from ebooks.api.serializers import EbookSerializer

class EbookListCreateAPIView(mixins.ListModelMixin,
                             mixins.CreateModelMixin,
                             generics.GenericAPIView):

    queryset = Ebook.objects.all()
    serializer_class = EbookSerializer

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

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

2. GenericAPIView

1) Basic settings

다음 속성들을 통해 View를 컨트롤함

  • queryset : View에서 객체를 반환하는 데 사용해야 하는 쿼리셋. 반드시 1) queryset 속성을 설정하거나, 2) get_queryset() 메서드로 override해서 사용해야 함.
  • serializer_class : 입력된 값을 validate하거나 deserialize하거나, 출력값을 serialize할 때 사용하는 serializer 클래스. 일반적으로 이 속성을 설정하거나 get_serializer_class()메소드로 override해서 사용해야 함
  • lookup_field : 개별 모델 인스턴스의 object 조회를 수행 할 때 사용해야하는 모델 필드. 기본값은 'pk'임. 하이퍼링크 된 API에 custom 값을 사용해야 하는 경우 API views와 serializer 클래스가 lookup필드를 설정해야 함.

2) Pagination

다음 속성을 통해 리스트뷰에서 페이지네이션을 컨트롤하게 된다.

  • pagination_class : 리스트 결과들을 페이지네이션할 때 사용되는 클래스임. 디폴트는 DEFAULT_PAGINATION_CLASS (rest_framework.pagination.PageNumberPagination 모듈안에) 세팅으로 결정됨.

3) Filtering

  • filter_backends : A list of filter backend classes that should be used for filtering the queryset. Defaults to the same value as the DEFAULT_FILTER_BACKENDS setting.

3. 주요 Mixins 소개 -> reference 문서

왜 믹스인 클래스를 사용할까? 만약 Django Rest Framework Mixin을 사용하지 않다면, 모든 기능을 모든 VIew에 직접, 반복적으로 구현해야할 것이다. 하지만 API를 작업할 때 목록을 보여주거나, 생성, 삭제, 수정 등은 항상 사용되는 반복적인 일이다. 이러한 반복적인 기능을 하나의 Mixin 클래스로 제공한다면 반복적인 일을 줄여주고 가독성, 생산성을 높여줄 수 있다.

단, Mixin 클래스에 존재하는 메소드나 속성을 상속받는 클래스에서 사용할 경우 믹스인 클래스의 메소드가 오버라이딩되어 의도하지 않게 작동할 수 있으니 주의해야한다.

1) ListModelMixin

  • Queryset을 리스팅하는 믹스인
  • .list(request, *args, **kwargs) 메소드로 호출하여 사용
  • GenericAPIView의 self.filter_queryset, self.get_queryset, self.get_serializer 등의 메소드를 활용해 데이터베이스에 저장되어 있는 데이터들을 목록 형태로 response body로 리턴
  • 성공 시, 200 OK response 리턴

2) CreateModelMixin

  • 모델 인스턴스를 생성하고 저장하는 역할을 하는 믹스인
  • .create(request, *args, **kwargs) 메소드로 호출하여 사용
  • 성공 시, 201 Created 리턴
  • 실패 시, 400 Bad Request 리턴

3) RetrieveModelMixin

  • 존재하는 모델 인스턴스를 리턴해 주는 믹스인
  • .retrieve(request, *args, **kwargs) 메소드로 호출하여 사용
  • 성공 시, 200 OK response 리턴
  • 실패 시, 404 Not Found 리턴

4) UpdateModelMixin

  • 모델 인스턴스를 수정하여 저장해 주는 믹스인
  • .update(request, *args, **kwargs) 메소드로 호출하여 사용
  • 부분만 변경하고자 할 경우, .partial_update(request, *args, **kwargs)메소드를 호출하여야 하며, 이 때 요청은 HTTP PATCH requests여야 함
  • 성공 시, 200 OK response 리턴
  • 실패 시, 404 Not Found 리턴

5) DestoryModelMixin

  • 모델 인스턴스를 삭제하는 믹스인
  • .destroy(request, *args, **kwargs) 메소드로 호출하여 사용
  • 성공 시, 204 No Content 리턴
  • 실패 시, 404 Not Found 리턴

(Q&A)

View -> APIView -> GenericAPIView로 View클래스의 기능이 더 많아지는데, 각 View별로 어떤 차이점이 있는가?

Mixins 상속

APIView 는 위에서 봤듯이 각 request method 마다 직접 serializer 처리를 해주었습니다.

하지만 이러한 부분들은 많이 사용되므로 여러 serializer 에 대해서 중복이 발생합니다.

따라서 rest_framework.mixins 에서는 이러한 기능들이 미리 구현이 되어 있습니다.

  • CreateModelMixin
  • ListModelMixin
  • RetrieveModelMixin
  • UpdateModelMixin
  • DestroyModelMixin

이름이 굉장히 직관적이므로 각각에 대한 자세한 설명은 생략하겠습니다.

querysetserializer_class 를 지정해주기만 하면 나머지는 상속받은 Mixin 과 연결해주기만 하면 됩니다.

# views.py

from rest_framework.response import Response
from rest_framework import generics
from rest_framework import mixins
from .models import Post
from .serializers import PostSerializer

class PostListMixins(mixins.ListModelMixin, mixins.CreateModelMixin,generics.GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request)
    
    def post(self, request, *args, **kwargs):
        return self.create(request)

class PostDetailMixins(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    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.delete(request, *args, **kwargs)

이에 대한 urls 입니다.

# urls.py

from django.urls import path, include
from . import views

urlpatterns = [
    # Mixin
    path('mixin/post/', views.PostListMixins.as_view()),
    path('mixin/post/<int:pk>/', views.PostDetailMixins.as_view()),
]

결과 화면은 위와 동일하므로 생략하도록 하겠습니다.

generics APIView

Mixin 을 상속함으로서 반복되는 내용을 많이 줄일 수 있었습니다. 하지만 여러 개를 상속해야 하다보니 가독성이 떨어집니다. 다행히도 rest_framework 에서는 저들을 상속한 새로운 클래스를 정의해놨습니다.

총 9개의 클래스로 다음과 같습니다.

  • generics.CreateAPIView : 생성
  • generics.ListAPIView : 목록
  • generics.RetrieveAPIView : 조회
  • generics.DestroyAPIView : 삭제
  • generics.UpdateAPIView : 수정
  • generics.RetrieveUpdateAPIView : 조회/수정
  • generics.RetrieveDestroyAPIView : 조회/삭제
  • generics.ListCreateAPIView : 목록/생성
  • generics.RetrieveUpdateDestroyAPIView : 조회/수정/삭제

하나만 예시로 살펴보면 위에서 해주었던 Mixin 과 GenericAPIView 상속, 각 request method 에 대한 연결을 해주고 있습니다.

# rest_framework/generics.py

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
    
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


이를 통해 코드를 많이 줄일 수 있습니다.

# views.py

from rest_framework import generics
from .models import Post
from .serializers import PostSerializer

class PostListGenericAPIView(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostDetailGenericAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

urls 와 결과 화면은 중복되므로 생략하도록 하겠습니다.

profile
박효영

1개의 댓글

comment-user-thumbnail
2023년 10월 19일

감사합니다.

답글 달기