
아래의 링크를 보고 거의 똑같이 요약한 글.
🌏[Django] APIView, Mixins, generics APIView, ViewSet을 알아보자
django는 view를 통해서 Http 요청을 처리한다.
view를 작성하는 방법은 다양하다.
크게는 FBV(Function-Based Views), CBV(Class-Based Views)로 나눌 수 있다.
이러한 방식으로 API를 만들 수도 있지만 django REST framework를 사용하는 방법도 있다.
APIView는 CBV에 대응되며 api_view는 FBV에 대응된다.
하지만 공통적으로 두 가지 모두 view에 기본 설정을 부여하는 역할을 한다.
부여하는 기본 설정은 아래와 같다. 아래의 내용이 default값이 된다.
renderer_classes=: 직렬화 클래스 지정
JSON 직렬화: rest_framework.renderers.JSONRenderer
HTML 페이지 직렬화: rest_framework.renderers.TemplateHTMLRenderer
parser_classes=: 비직렬화 클래스 지정
JSON 포맷: rest_framework.parsers.JSONParser
FormParser: rest_framework.parsers.FormParser
MultiPartParser: rest_framework.parsers.MultiPartParser
authentication_classes=: 인증 클래스 지정
session 기반 인증: rest_framework.authentication.SessionAuthentication
Http basic 인증: rest_framework.authentication.BasicAuthentication
throttle_classes=:
기본값은 빈 튜플
permission_classes:
누구에게나 접근 허용: [rest_framework.permissions.AllowAny]
content_negotiation_class=: 요청에 따라 직렬화 또는 비직렬화 선택 / 같은 URL에 대한 요청에 대해 JSON응답 vs. HTML응답 판단: rest_framework.negotiation.DefaultContentNegotiation
versioning_class=: 요청 내녁에서 API 버전 정보를 탐지할 클래스 지정 / 요청 URL의 HEADER에서 버전 정보를 탐지하고 맞는 버전을 호출: None(버전 정보를 탐지하지 않음)
명시하지 않아도 기본값으로 들어가는 내용들이다. 필요 시 이를 명시하고 수정하여 상황에 맞게 쓸 수 있다.
APIView는 CBV의 일종이기 때문에 하나의 URL에 대한 처리만 가능하다.
get: 목록post: 생성get: pk(int)번째 자원의 내용put: pk(int)번째 자원에 대한 수정delete: pk(int)번째 자원을 삭제요청의 method에 맞게 멤버함수를 정의해두면 해당 method의 request가 들어올 때마다, 일치하는 멤버함수가 호출된다.
#example
class RestAPI(APIView):
def get(self, request):
pass
# get 요청이 들어왔을 때 수행할 기능
def post(self, request):
pass
# post 요청이 들어왔을 때 수행할 기능
def put(self, request):
pass
# put 요청이 들어왔을 때 수행할 기능
def delete(self, request):
pass
# delete 요청이 들어왔을 때 수행할 기능
FBV에 url을 붙일 때는 view의 이름(함수 이름)만 써주면 그만이었다.
... path('user/', views.user_list), ...CBV에 url을 붙일 때는 CBV이름.as_view()의 구조로 설정해야 한다.
... path('user/', views.user_list.as_view()), ...
rest_framework의 serializer를 사용한 스타일은 다음과 같다.
# views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Post
from .serializers import PostSerializer
# 포스팅 목록 및 새 포스팅 작성: APIView를 사용
class PostListAPIView(APIView):
# get 요청이 들어왔을 때
def get(self, request):
serializer = PostSerializer(Post.objects.all(), many=True)
return Response(serializer.data)
# post 요청이 들어왔을 때
def post(self, request):
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
from django.shortcuts import get_object_or_404
# 포스팅 내용, 수정, 삭제: 마찬가지로 APIView를 사용
class PostDetailAPIView(APIView):
# get_object가 작동하는 방식은 여기서 설명하지 못 함(ㅠㅠ)
def get_object(self, pk):
return get_object_or_404(Post, pk=pk)
# get 요청이 들어왔을 떄
def get(self, request, pk, format=None):
post = self.get_object(pk)
serializer = PostSerializer(post)
return Response(serializer.data)
# put 요청이 들어왔을 때
def put(self, request, pk):
post = self.get_object(pk)
serializer = PostSerializer(post, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# delete 요청이 들어왔을 때
def delete(self, request, pk):
post = self.get_object(pk)
post.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
이 코드는 수학좌님의 코드를 그대로 복붙해놓은 것이다. 더 자세한 내용은 링크링크.
django ORM과 DRF의 serializer, validator 기능을 이용하여 코드를 체계적으로 정리하고 있다.
코드를 보면 알 수 있듯이 APIView는 특정 method의 request가 들어올 때마다 따로따로 serializer=[...] 처리를 해준 것을 볼 수 있다. 결과적으로 코드의 중복이 기능의 중복을 불러온다.
따라서 이러한 약점(?)을 해결하는 기능을 rest_framework.mixins에서 제공하고 있다.
일단 종류부터 알아본다.
CreateModelMixin
ListModeMixin
RetrieveModelMixin
UpdateModelMixin
DestroyModelMixin
위와 같은 클래스가 mixins.CreateModelMixin, mxins.ListModeMixin... 이런 식으로 쓰이게 된다.
수학좌님께서는 '이름이 굉장히 직관적이므로 각각에 대한 자세한 설명은 생략하겠습니다'라고 설명하셨다.
하지만 이 부족한 머리통은 이름에서 어떤 냄새만 맡을 수 있을 뿐이고 내 예상과 다른 기능을할까봐 너무 두렵다.
예시로 제시해주신 관련 코드와 설명을 보면서 뭐가뭔지 탐구해보도록 한다.
# 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
# mixins.
# ListModelMixin, CreateModelMixin을 상속
class PostListMixins(mixins.ListModelMixin, mixins.CreateModelMixin,generics.GenericAPIView):
# 기본적으로 queryset은 Post model안의 객체를 모두 불러오고
queryset = Post.objects.all()
# serializer는 PostSerializer를 사용한다
# 일반적으로 serializers.py에 명시해뒀을 것이다
serializer_class = PostSerializer
# get 요청이 들어오면 list를 반환한다
# mixins.ListModelMixin
def get(self, request, *args, **kwargs):
return self.list(request)
# post 요청이 들어오면 새로은 객체를 생성한다
# mixins.CreateModelMixin
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)
읽다보니 의문이 생긴다.
하나같이 마지막 상속에GenericAPIView를 포함하고 있다.
pyyou님의 포스팅을 보고 알아본다.
pyyou님의 포스팅에서 심각하게 기가막히게 설명을 너무 잘해주셨기 때문에 아래의 내용도 수학좌님의 포스팅을 거의 복붙하다시피한 위의 내용처럼 복붙해놓은 수준이다.
다시 한번 절하고 배우겠습니다. 🙇🏻♂️
django REST framework를 사용하여 GenericAPIView에 ListModeMixin, CreateModMixin등을 결합해 다양한 APIView를 구현할 수 있다.
GenericAPIView는 CRUD에서 공통적으로 사용되는 속성을 제공한다Mixins는 CRUD 중의 특정한 기능을 수행하는 메소드를 제공한다GenericAPIView와 Mixins로 정확한 기능 구현이 어려울 때, 오버라이드를 통한 커스터마이징이 가능하다< 기본 속성 >
queryset: View에서 객체를 반환하는 데 사용해야 하는 쿼리셋
반드시 queryset 속성을 설정해주거나 get_queryset()으로 오버라이드하여 사용해야 한다
serializer_class: 받은 값을 검증하거나(validate) 역직렬화(deserialize)하거나 출력값을 직렬화(serialize)할 때 사용한다. 일반적으로 serializer_class를 사용하거나 get_serializer_class() 메소드를 오버라이드하여 사용해야 한다
lookup_field: 개별 모델의 인스턴스의 object를 조회할 때 사용해야하는 모델 필드이다. 기본값은 'pk'. 하이퍼링크 된 API에 커스텀값을 사용해야 하는 경우 APIView와 serializer클래스가 lookup필드를 설정해야 한다.
🔎 Mixin을 상속함으로써 반복되는 코드를 상당 줄일 수 있지만, 여러 개의 클래스를 상속해야 하다보니 코드가 깔끔해보이지 않을 수 있다.
이럴 때 이용하는 것이
generics APIView라고 할 수 있다.
generics.CreateAPIView: 생성
generics.ListAPIView: 목록
generics.RetrieveAPIView: 조회
generics.DestroyAPIView: 삭제
generics.UpdateAPIView: 수정
generics.RetrieveUpdateAPIView: 조회/수정
generics.RetrieveDestroyAPIView: 조회/삭제
generics.ListCreateAPIView: 목록/생성
generics.RetrieveUpdateDestroyAPIView: 조회/수정/삭제
위의 mixin 상속 부분의 코드에서 쓰임을 확인할 수 있다.
mixins.ListModelMixin.list(request, *args, **kwargs) 메소드로 호출한다GenericAPIView의 self.fiter_queryset, self.get_queryset, self.get_serializer등의 메소드를 활용하여 데이터를 목록 형태로 return200 OKmixins.CreateModelMixin.create(request, *args, **kwargs) 메소드로 호출한다201 Created / 실패: 400 Bad Request mixins.RetrieveModelMixin.retrieve(request, *args, **kwargs) 메소드로 호출하여 사용200 Created / 실패: 400 Bad Request mixins.UpdateModelMixin.update(request, *args, **kwargs) 메소드로 호출하여 사용.partial_update(request, *args, **kwargs) 메소드를 호출하여야 하며 이 때, 요청은 HTTP PATCH requests이어야 한다200 OK / 실패: 404 Not Foundmixins.DestoryModelMixin .destroy(request, *args, **kwargs) 메소드를 호출하여 사용204 No Content / 실패: 404 Not Found이렇게 GenericAPIView와 Mixins의 관계까지 알아볼 수가 있었다.
generic으로 갈 수록 그 기능을 간편하게 정확히 쓸 수 있는 것 같고 API_View로 갈 수록 직접 짜야 하는 코드는 많아지지만 자유도가 높아지는 것 같다.
django에서 제공하는 것이 굉장이 많게 보이다 보니 정리하고 사용해 보는 데에 시간이 꽤 드는 느낌이다.
요령을 찾으려면 다양하게 많은 API를 만들어 봐야겠단 생각이 든다.
나머지 내용도 추가해서 정리해야겠다.
도움주신 분들
수학좌: [Django] APIView, Mixins, generics APIView, ViewSet을 알아보자
phyyou님: DRF 공부하기 (7) :: GenericAPIView와 Mixins
현암 코딩님: 4-2 Generic Views (Mixin,Concrete)