jwj3400
[backend] Django-rest-framework 각 종 View 본문
django에서는 FBV(함수기반뷰), CBV(클래스기반뷰)를 통해서 API를 만들 수 있지만, rest_framework를 사용하면 보다 효율적으로 API를 만들 수 있음
상속받는 클래스의 추상화 정도
- APIView < mixins < generics APIView < ViewSet
APIView 상속
- APIView는 CBV, @api_view는 FBV에 대
- 하나의 CBV이므로 하나의 URL만 처리
- 각 요청 method (get, post, put, delete)에 맞게 멤버함수를 구현하면, 해당 method 요청이 들어올 때 호출
- 호출이 되면 호출 직전 다음을 처리 (아래는 지정 안해 줄 시 default로 지정되는 값)
- 직렬화/비직렬화 (renderer_classes=직렬화 클래스 지정, parser_classes=비직렬화 클래스 지정)
default: JSON 직렬화: rest_framework.renderers.JSONRenderer
default: HTML 페이지 직렬화: rest_framework.renderers.TemplateHTMLRenderer
default: JSON 포맷 비직렬화: rest_framework.parsers.JSONParser
default: FormParser 비직렬화 : rest_framework.parsers.FormParser
default: MultiPartParser 비직렬화 : rest_framework.parsers.MultiPartParser - 인증체크 (authentication_classes=인증 클래스 지정)
default: session 기반 인증: rest_framework.authentication.SessionAuthentication
default: Http basic 인증: rest_framework.authentication.BasicAuthentication - 사용량 제한체크 (throttle_classes=)
default: 빈 튜플 - 권한체크 (permission_classes=권한 클래스 지정)
default: 기본 권한 클래스: rest_framework.permissions.AllowAny - 요청한 버전 체크(versioning_class=)
default: 버전정보 탐지 x
- 직렬화/비직렬화 (renderer_classes=직렬화 클래스 지정, parser_classes=비직렬화 클래스 지정)
- APIView는 특정 method의 request가 들어올 때마다 따로따로 serializer=[...] 처리를 해줘야 됨
=> 같은 작업을 반복해야되는 단점이 있음
# 포스팅 목록 및 새 포스팅 작성
class PostListAPIView(APIView):
def get(self, request):
serializer = PostSerializer(Post.objects.all(), many=True)
return Response(serializer.data)
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
# 포스팅 내용, 수정, 삭제
class PostDetailAPIView(APIView):
def get_object(self, pk):
return get_object_or_404(Post, pk=pk)
def get(self, request, pk, format=None):
post = self.get_object(pk)
serializer = PostSerializer(post)
return Response(serializer.data)
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)
def delete(self, request, pk):
post = self.get_object(pk)
post.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
GenericAPIView 상속 & Mixins
- Django Rest Framework에서는 GenericAPIView에 Mixins 클래스를 결합해 API를 구현할 할 수 있음
- GenericAPIView는 CRUD에서 공통적으로 사용되는 속성을 제공
Mixin은 CRUD 중 특정 기능을 수행하는 메소드를 제공 - GenericAPIView를 상속 받을때에는 무조건 가장 뒤에 와야 함
GenericAPIView
- queryset : View에서 객체를 반환하는 데 사용해야 하는 쿼리셋.
반드시 1) queryset 속성을 설정하거나, 2) get_queryset() 메서드로 override해서 사용해야 함. - serializer_class : 입력된 값을 검증(validate)하거나 deserialize하거나, 출력값을 serialize할 때 사용하는 serializer 클래스. 일반적으로 1) serializer_class 속성을 설정하거나 2) get_serializer_class() 메서드로 override해서 사용해야 함
- lookup_field : 개별 모델 인스턴스의 object 조회를 수행 할 때 사용해야하는 모델 필드. 기본값은 'pk'임. 하이퍼링크 된 API에 custom 값을 사용해야 하는 경우 API views와 serializer 클래스가 lookup필드를 설정해야 함.
Mixins
- 메서드를 상속받은 Mixin과 연결해주는 형태로 작동
아래와 같은 클래스가 mixins.CreateModelMixin, mxins.ListModeMixin... 식으로 상속되어 사용- CreateModelMixin : 모델 인스턴스를 생성하고 저장하는 역할을 하는 믹스인, 성공시 201 return
ListModeMixin : Queryset을 리스팅하는 믹스인, 성공시 200 return
RetrieveModelMixin : 존재하는 모델 인스턴스를 리턴해 주는 믹스인, 성공시 200 return
UpdateModelMixin
DestroyModelMixin : 성공시 204 return - genericview에는 mixins을 내부적으로 사용하여 여러 view를 제공
generics.CreateAPIView : 생성
generics.ListAPIView : 목록
generics.RetrieveAPIView : 조회
generics.DestroyAPIView : 삭제
generics.UpdateAPIView : 수정
generics.RetrieveUpdateAPIView : 조회/수정
generics.RetrieveDestroyAPIView : 조회/삭제
generics.ListCreateAPIView : 목록/생성
generics.RetrieveUpdateDestroyAPIView : 조회/수정/삭제
- CreateModelMixin : 모델 인스턴스를 생성하고 저장하는 역할을 하는 믹스인, 성공시 201 return
# 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)
#generics.
class PostListGenericAPIView(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostDetailGenericAPIView(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
- genericview를 사용할 경우 mixins 를 썼을 때 보다 많이 간소화 되었지만 queryset 과 serializer_class 가 공통적임에도 불구하고 따로 기재해야함
# 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()),
]
ViewSet
- generics APIView는 queryset 과 serializer_class 가 공통적인데도 불구하고 따로 기재해주어야 함
이를 한 번에 처리해주는게 바로 ViewSet - viewsets.ReadOnlyModelViewset과 viewsets.ModelViewset 둘 중하나를 상속받음
- viewset class로 CBV를 만들면 내장된 로직으로 CRUD를 처리 (Apiview는 하나씩 만들어줘야하고, Mixin은 genericview는 관련된 view를 상속받아 각각 class로 만들어야됨 )
- @action(detail=False, methods=['POST'])과 같은 방식으로 내장된 로직이 아닌 직접 구현한 로직으로 crud가 처리되게 할 수 있음
- @action 데코레이터를 사용하여 추가한 커스텀 액션(signup이나 login과 같은)은 해당 뷰셋의 기본 액션과 별개로 동작함. 따라서 @action으로 추가한 액션은 해당 뷰셋의 URL에 더해진 경로로 호출
- 예를 들어, signup 액션이 UserViewSet에 추가되었다면, 해당 액션을 호출하기 위해서는 /user/signup/와 같은 URL을 사용
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
- ModelViewSet는 기본적으로 CRUD(Create, Retrieve, Update, Delete) 동작을 제공
-
- List (GET): 모델의 모든 객체를 조회합니다. GET 요청을 받아서 list 메서드를 실행합니다.
- Create (POST): 새로운 객체를 생성합니다. POST 요청을 받아서 create 메서드를 실행합니다.
- Retrieve (GET): 특정 객체의 세부 정보를 조회합니다. GET 요청을 받아서 retrieve 메서드를 실행합니다.
- Update (PUT or PATCH): 특정 객체의 정보를 업데이트합니다. PUT 또는 PATCH 요청을 받아서 update 메서드를 실행합니다.
- Destroy (DELETE): 특정 객체를 삭제합니다. DELETE 요청을 받아서 destroy 메서드를 실행합니다
- 기본적으로 이러한 액션들은 queryset과 serializer_class에 의존하여 동작하며, 해당 모델과 시리얼라이저를 기반으로 작동
- viewset의 내장된 CRUD 동작을 하게 하려면 아래와 같이 url 설정시 DefaultRouter을 이용해야 함
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
path('', include(router.urls)),
]
- 내장된 CRUD가 아닌 @action으로 직접 구현한 viewset을 활용하려면 아래와 같이 as_view를 활용하여 url 설정
from django.urls import path
from .views import UserViewSet, Login, Logout
user_view = UserViewSet.as_view({
'post': 'signup',
'delete':'delete',
'get': 'test_get'
})
urlpatterns = [
path('users/', user_view)
]
즉, ViewSet은 모델 전체의 CRUD를 다 구현해야 할 때 사용하는 경우 이점이 있으며 있때 defaultRouter를 써야 모든 내장기능을 사용가능 ViewSet을 사용하더라도 직접 구현한 로직만 사용하려면 as_view를 사용하고 이런 경우 crud의 일부만 구현해서 사용가능함 (ex) 로그인 관련 view는 post만 변형해서 사용하면되지 crud를 전부 사용할 필요가 없기에 as_view로 처리하는 것이 나음)