Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

jwj3400

[backend] Django-rest-framework 각 종 View 본문

카테고리 없음

[backend] Django-rest-framework 각 종 View

jwj3400 2023. 10. 10. 20:18

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로 지정되는 값)
    1. 직렬화/비직렬화 (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
    2. 인증체크 (authentication_classes=인증 클래스 지정)
      default:  session 기반 인증: rest_framework.authentication.SessionAuthentication
      default: Http basic 인증: rest_framework.authentication.BasicAuthentication
    3. 사용량 제한체크 (throttle_classes=)
      default: 빈 튜플
    4. 권한체크 (permission_classes=권한 클래스 지정)
      default: 기본 권한 클래스: rest_framework.permissions.AllowAny
    5. 요청한 버전 체크(versioning_class=)
      default: 버전정보 탐지 x
  • 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 : 조회/수정/삭제

 

# 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) 동작을 제공
    1. List (GET): 모델의 모든 객체를 조회합니다. GET 요청을 받아서 list 메서드를 실행합니다.
    2. Create (POST): 새로운 객체를 생성합니다. POST 요청을 받아서 create 메서드를 실행합니다.
    3. Retrieve (GET): 특정 객체의 세부 정보를 조회합니다. GET 요청을 받아서 retrieve 메서드를 실행합니다.
    4. Update (PUT or PATCH): 특정 객체의 정보를 업데이트합니다. PUT 또는 PATCH 요청을 받아서 update 메서드를 실행합니다.
    5. Destroy (DELETE): 특정 객체를 삭제합니다. DELETE 요청을 받아서 destroy 메서드를 실행합니다
  • 기본적으로 이러한 액션들은 querysetserializer_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로 처리하는 것이 나음)