#FBV#CBV(apiview>mixin>generics>viewset)
views.py에서 API 통신을 관리하는데 필요한 핵심 로직이 들어있다. 다양한 요청을 고객으로 받았을 때, 데이터를 가지고 어떻게 행동해야할지가 적여있는 것이다.
다양한 요청이라고 하지만, 알고보면 고객이 요청하는 종류는 다음과 같이 정형화되어있다.
그래서 Djangdo REST Framework는 이를 표준화시켜 개발자가 이용하기 쉽게 잘 정리해두었다. 우리는 이를 가져다 쓰기만 하면 된다.
목차
FBV
CBV
1. APIView(기본형태)
2-1. MIXINS
2-2. GENERICS #MIXINS 조합
3. VIEWSET & ROUTERS
@api_view(['GET', 'POST'])
def mymodel_list(request):
if request.method == 'GET':
queryset = MyModel.objects.all()
serializer = MyModelSerializer(queryset, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = MyModelSerializer(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)
@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
def mymodel_detail(request, pk):
try:
instance = MyModel.objects.get(pk=pk)
except MyModel.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = MyModelSerializer(instance)
return Response(serializer.data)
elif request.method == 'POST':
serializer = MyModelSerializer(instance, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'PUT':
serializer = MyModelSerializer(instance, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'PATCH':
serializer = MyModelSerializer(instance, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
가장 자유롭지만 자세히 코드를 작성해줘야 하는 것은 APIView이다.
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import YourModel
from .serializers import YourModelSerializer
class YourModelRetrieveUpdateDestroyAPIView(APIView):
lookup_field = 'id' # 여기서 'id'는 객체를 조회할 때 사용할 필드
def get_object(self, pk):
try:
return YourModel.objects.get(id=pk)
except YourModel.DoesNotExist:
raise status.HTTP_404_NOT_FOUND
def get(self, request, id, format=None):
instance = self.get_object(id)
serializer = YourModelSerializer(instance)
return Response(serializer.data)
def put(self, request, id, format=None):
instance = self.get_object(id)
serializer = YourModelSerializer(instance, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def patch(self, request, id, format=None):
instance = self.get_object(id)
serializer = YourModelSerializer(instance, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id, format=None):
instance = self.get_object(id)
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
이 코드는 너무 길다. 중복되는 부분을 줄일 필요가 있다. 그렇게 mixins가 등장한다.
from rest_framework import generics
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
from .models import YourModel
from .serializers import YourModelSerializer
class YourModelRetrieveUpdateDestroyAPIView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, generics.GenericAPIView):
queryset = YourModel.objects.all()
serializer_class = YourModelSerializer
lookup_field = 'id' # 여기서 'id'는 객체를 조회할 때 사용할 필드
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)
generics는 mixins를 조합한 것에 불과하다. GenericAPIView의 기본 형태는 django-rest-framework/rest_framework/generics.py에서 살펴볼 수 있듯, 다음과 같다.
class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
def get_queryset(self):
pass
def get_object(self):
pass
def get_serializer(self, *args, **kwargs):
pass
def get_serializer_class(self):
pass
def get_object(self):
어쩌고 저쩌고
def post(self, request):
어쩌고 저쩌고
이를 기반으로 mixin들의 조합은 9가지가 나오며, 이를 하나씩 보면 다음과 같다.
1. class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):
: post(self, request, *args, **kwargs): → create(self, request, *args, **kwargs):
: 전자는 CreateAPIView에 정의된 것이며, 후자를 CreateModelMixin에서 가져온 것
2. class ListAPIView(mixins.ListModelMixin, GenericAPIView): #전체 조회
: PostSerializer(instance=, many=True)
: get(self, request, *args, **kwargs): → list(self, request, *args, **kwargs):
: 전자는 ListAPIView에 정의된 것이며, 후자를 ListModelMixin에서 가져온 것
3. class RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView): #1개 조회
: PostSerializer(instance=, many=False)
: get(self, request, *args, **kwargs): → retrieve(self, request, *args, **kwargs):
4. class UpdateAPIView(mixins.UpdateModelMixin, GenericAPIView):
: put(self, request, *args, **kwargs): → update(self, request, *args, **kwargs):
: patch(self, request, *args, **kwargs): → partial_update(self, request, *args, **kwargs):
5. class DestroyAPIView(mixins.DestroyModelMixin, GenericAPIView):
: delete(self, request, *args, **kwargs): → destroy(self, request, *args, **kwargs):
6. class ListCreateAPIView(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView):
7. class RetrieveUpdateAPIView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericAPIView):
8. class RetrieveDestroyAPIView(mixins.RetrieveModelMixin, mixins.DestroyModelMixin, GenericAPIView):
9. class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin,
mixins.DestroyModelMixin, GenericAPIView):
예제 코드를 보자
from rest_framework import generics
from .models import YourModel
from .serializers import YourModelSerializer
class YourModelRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
queryset = YourModel.objects.all()
serializer_class = YourModelSerializer
lookup_field = 'id' # 여기서 'id'는 객체를 조회할 때 사용할 필드
아예 모든 요청 Method들을 합쳐서 한번에 이용할 수도 있다. 이를 Viewset이라고 부른다.
// views.py
from rest_framework import viewsets
from .models import YourModel
from .serializers import YourModelSerializer
class YourModelViewSet(viewsets.ModelViewSet):
queryset = YourModel.objects.all()
serializer_class = YourModelSerializer
lookup_field = 'id'
ViewSet & GenericViewSet - rest_framework/viewsets.py
ViewSet = ViewSetMixin + APIView
- 코드를 커스텀해서 자유롭게 사용할 때 주로 활용
- router에 등록해서 사용하니 list(), retrieve(), create(), update(), destroy() 사용가능
GenericViewSet = ViewSetMixin + GenericAPIView
= ViewSetMixin + GenericAPIView 멤버함수들 + APIView
- GenericAPIView는 APIView만 상속 받음
- CRUD 위주의 표준화된 패턴으로 개발할 때 주로 활용
- perform_create()는 CreateModelMixin의 멤버함수
- list()는 ListModelMixin의 멤버함수
- retrieve()는 RetrieveModelMixin의 멤버함수
- perform_update()는 UpdateModelMixin의 멤버함수
- perform_destroy()는 DestroyModelMixin의 멤버함수
다만 viewset을 쓸 땐 urls.py를 작성하는 방식이 달라진다. drf에서 제공하는 라우터를 urls.py에 등록해야하는 것이다.
// urls.py
from rest_framework.routers import DefaultRouter
from .views import YourModelViewSet
router = DefaultRouter()
router.register(r'yourmodel', YourModelViewSet, basename='yourmodel')
urlpatterns += router.urls
- SimpleRouter에 요청 매소드별로 함수명을 매핑시키는 기능이 있다.
DRF 공식문서 : https://www.django-rest-framework.org/api-guide/generic-views/
DRF 정리문서 : https://www.cdrf.co/