[DRF] - Mixin, Generic, ViewSet

KimJiHong·2023년 12월 11일
1

REST API

목록 보기
3/6
post-thumbnail

Mixin

DRF에서 APIView(CBV) 또는 api_view(FBV) 를 사용하여 API를 구축 했을 때,
CRUD 작업에 대한 메소드를 전부 구현해서 코드의 길이가 길어지고 중복된 코드가 발생할 수 있다.

DRF는 중복된 코드를 줄이고 코드 재사용성을 늘리기 위해 일반적으로 사용되는 CRUD 로직을 Mixin 클래스로 구현되어 있다.

Mixin 클래스는 get, post와 같은 HTTP 메소드 Handler를 직접 정의하는게 아니라 action 메소드를 통해 CRUD 작업을 정의할 수 있다.

한번 GET, POST 메소드에 대한 게시글 리소스 작업을 APIView와 Mixin 클래스로 비교해보자.

# boards/views.py

# Example.1 : APIView
class ApiViewExample(APIView):
	def get(self, reqeust): # 너무 긴 pagination ..
    	queryset = PostModel.objects.all()
        
        page = self.pagination_class()
        posts_data_page = page.paginate_queryset(queryset, request)
        if posts_data_page:
        	serializer = PostModelSerializer(posts_data_page, many=True)
            return page.get_paginated_response(serializer.data)
            
        serializer = UserSerializer(instance, many= True)
        return Response(serializer.data, status.HTTP_200_OK)
        
    def post(self, request):
    	serializer = PostModelSerializer(data= request.data)
        serializer.is_valid(raise_exception= True)
        serializer.save()
        return Response(serializer.data, status= status.HTTP_201_CREATED)
        
    
# Example.2 : Mixin
class MixinExample(mixins.ListModelMixin,
				   mixins.CreateModelMixin,
				   GenericAPIView):
	queryset = PostModel.objects.all()
    serializer_class = PostModelSerializer
    
    def get(self, request, *args, **kwargs):
    	return self.list(request, *args, **kwargs) # list Action Method
        
    def post(self, request, *args, **kwargs):
    	return self.create(request, *args, **kwargs) # create Action Method

Mixin 클래스로 구현시 APIView를 상속한 GenericAPIView를 통해 메소드마다 중복 사용되는 querysetserializer를 지정

CRUD 작업의 Action 즉, 예제에서의 .list(), .create() 와 같은 Action 메소드 하나로 빠르게 구현이 가능하다.

Mixin Action method And GenericAPIView

Action Method

Mixin 클래스의 Action 메소드는 총 5개가 정의되어 있다.

  • CreateModelMixin
    • 새로운 리소스를 생성하고 저장하는 Mixin 클래스.
    • 리소스 생성 성공시 response body에 serializer 데이터를 포함하여 201 응답.
    • 리소스 생성 실패시 response body에 error 정보를 포함하여 400 응답.
  • ListModelMixin
    • 리소스 리스트를 선택적으로 페이징 처리 후 조회하는 Mixin 클래스.
    • response body에 serializer 데이터를 포함하여 200 응답.
  • RetrieveModelMixin
    • 특정 리소스를 조회하는 Mixin 클래스.
    • response body에 serializer 데이터를 포함하여 200 응답.
  • UpdateModelMixin
    • 특정 리소스를 수정하는 Mixin 클래스.
    • 리소스 수정 성공시 response body에 serializer 데이터를 포함하여 200 응답.
    • 리소스 수정 실패시 response body에 error 정보를 포함하여 400 응답.
  • DestroyModelMixin
    • 특정 리소스를 삭제하는 Mixin 클래스.
    • 리소스 삭제 성공시 204 응답.
    • 리소스 삭제 실패시 404 응답.

구현된 Action method 는 DRF-mixins.py 에서 자세히 볼 수 있다.

GenericAPIView

Mixin 클래스 사용시 중요한 것은

Mixin 클래스 자체는 단순히 일반적으로 사용되는 CRUD 작업을 Action 메소드로써 작성한 것이지 Mixin 클래스 그 자체만으로는 API 뷰가 아니다.

# rest_framework/mixins.py
class CreateModelMixin: # 상속 받은 것이 없음!
	# ... logic ...
    
# rest_framework/generics.py
class GenericAPIView(views.APIView): # APIView 상속
	# ... get_queryset ...
    # ... get_object ...
    # ... get_serializer ...
    # ... paginate_queryset ...
    # ... etc. ...

따라서 APIView 클래스를 상속받은 GenericAPIView 를 사용하여 Mixin 클래스를 조합해 API 기능을 구현해야 한다.

구현된 GenericAPIViewDRF-generic.py 에서 자세히 볼 수 있다.


Generic

Generic viewMixin 클래스 사용시 상속하는 클래스가 많다는 것과 각 HTTP 메소드마다 Action을 연결해야하는 번거로움을 줄이기 위해 특정 작업을 MixinGenericAPIView의 조합으로 미리 정의한 클래스이다.

직접 Detail-API 를 Mixin 클래스와 Generic 클래스로 각각 구현해서 비교해보자.

# boards/views.py

# Example.1 : Mixin & GenericAPIView
class MixinExample(mixins.RetrieveModelMixin,
				   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
				   GenericAPIView):
	queryset = PostModel.objects.all()
    serializer_class = PostModelSerializer
    
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
        
        
# Example.2 : Generic View
class GenericExample(generic.RetrieveUpdateDestroyAPIView):
	queryset = PostModel.objects.all()
    serializer_class = PostModelSerializer

READ, UPDATE, DELETE 작업이 RetrieveUpdateDestroyAPIView 를 상속함으로 각 HTTP 메소드와 Action 메소드 연결하는 번거로움이 사라지고 코드가 단 3줄로 변경되었다.

DRF'S Generic APIView

DRF 에서는 특정 CRUD 작업을 미리 정의한 여러가지 generic APIView가 있다.

  • CreateAPIView - CREATE
  • ListAPIView - READ(List)
  • RetrieveAPIView - GET(Retrieve)
  • DestroyAPIView - DELETE
  • UpdateAPIView - UPDATE
  • ListCreateAPIView - READ(List), CREATE
  • RetrieveUpdateAPIView - READ(Retrieve), UPDATE
  • RetrieveDestroyAPIView - READ(Retrieve), DELETE
  • RetrieveUpdateDestroyAPIView - READ(Retrieve), UPDATE, DELETE

제세한 Generic APIView는 DRF-generic.py 에서 볼 수 있다.


ViewSet

APIView를 사용할 때보다 Mixin 클래스를 이용했을 때 간결해졌고,
Mixin 클래스를 사용했을 때보다 generic 클래스를 이용했을 때 간결해졌다.

하지만, 보통 REST API를 구축할 때 여러 객체에 대한 작업 ListAPI와 단일 객체에 대한 작업 DetailAPI로 구현한다.

  • ListAPI(ex. /api/posts/<int:pk>) - GET, POST
  • DetailAPI(ex. /api/posts) - GET, PUT, DELETE

DRF의 ViewSet은 2개의 URL로 구현된 5가지 작업을 하나의 클래스에서 처리할 수 있도록 도와준다.

또, URL Router를 통해 2개로 나눠진 URL을 하나의 URL로 구현이 가능하다.

router = DefaultRouter()
router.register('post', YourModelViewSet, basename='posts')

# Route URL -> '/post/' and '/post/<int:pk>/'

한번 전체 CRUD 작업을 ViewSet으로 구현해보자.

# boards/views.py

class PostModelViewSet(viewsets.ModelViewSet):
	queryset = PostModel.object.all()
    serializer_class = PostModelSerializer
    
    
# boards/urls.py

router = DefaultRouter(trailing_slash= False) # 마지막 슬래시 제거 옵션 추가
router.register('post', YourModelViewSet, basename='posts')

urlpatterns = [
    path('', include(router.urls)),
    # ...
]

Summary & Ref.

Summary

  • API 설계시 APIView만 사용하면 코드가 너무 길다. -> Mixin & GenericAPIView
  • Mixin 클래스 사용시 Action 메소드 연결이 번거롭다. -> Generic
  • 하나의 URL 또는 하나의 클래스에서 구현하고 싶다. -> ViewSet, router

Ref.

https://github.com/encode/django-rest-framework/tree/master
https://hyun-am-coding.tistory.com/entry/4-2-Generic-Views-MixinConcrete

profile
https://h0ng.dev

0개의 댓글

관련 채용 정보