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를 통해 메소드마다 중복 사용되는 queryset과 serializer를 지정
CRUD 작업의 Action 즉, 예제에서의 .list()
, .create()
와 같은 Action 메소드 하나로 빠르게 구현이 가능하다.
Mixin 클래스의 Action 메소드는 총 5개가 정의되어 있다.
구현된 Action method 는 DRF-mixins.py 에서 자세히 볼 수 있다.
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 기능을 구현해야 한다.
구현된 GenericAPIView 는 DRF-generic.py 에서 자세히 볼 수 있다.
Generic view는 Mixin 클래스 사용시 상속하는 클래스가 많다는 것과 각 HTTP 메소드마다 Action을 연결해야하는 번거로움을 줄이기 위해 특정 작업을 Mixin과 GenericAPIView의 조합으로 미리 정의한 클래스이다.
직접 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 에서는 특정 CRUD 작업을 미리 정의한 여러가지 generic APIView가 있다.
제세한 Generic APIView는 DRF-generic.py 에서 볼 수 있다.
APIView를 사용할 때보다 Mixin 클래스를 이용했을 때 간결해졌고,
Mixin 클래스를 사용했을 때보다 generic 클래스를 이용했을 때 간결해졌다.
하지만, 보통 REST API를 구축할 때 여러 객체에 대한 작업 ListAPI와 단일 객체에 대한 작업 DetailAPI로 구현한다.
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)),
# ...
]
https://github.com/encode/django-rest-framework/tree/master
https://hyun-am-coding.tistory.com/entry/4-2-Generic-Views-MixinConcrete