django View 알아보기

0

장고 프로젝트

목록 보기
14/15
post-thumbnail

아래의 링크를 보고 거의 똑같이 요약한 글.
🌏[Django] APIView, Mixins, generics APIView, ViewSet을 알아보자

수학좌 언제나 감사합니다. 🙇🏻‍♂️


django는 view를 통해서 Http 요청을 처리한다.
view를 작성하는 방법은 다양하다.

크게는 FBV(Function-Based Views), CBV(Class-Based Views)로 나눌 수 있다.

이러한 방식으로 API를 만들 수도 있지만 django REST framework를 사용하는 방법도 있다.

APIVIEW 클래스 & API_VIEW 장식자

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

APIView는 CBV의 일종이기 때문에 하나의 URL에 대한 처리만 가능하다.

  1. URL: .. /post/ 에 대한 CBV
  • get: 목록
  • post: 생성
  1. URL: ../post/<int:pk>/ 에 대한 CBV
  • 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에서 제공하고 있다.

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님의 포스팅을 보고 알아본다.

GenericAPIView와 Mixins

pyyou님의 포스팅에서 심각하게 기가막히게 설명을 너무 잘해주셨기 때문에 아래의 내용도 수학좌님의 포스팅을 거의 복붙하다시피한 위의 내용처럼 복붙해놓은 수준이다.

다시 한번 절하고 배우겠습니다. 🙇🏻‍♂️

django REST framework를 사용하여 GenericAPIViewListModeMixin, CreateModMixin등을 결합해 다양한 APIView를 구현할 수 있다.

  • GenericAPIViewCRUD에서 공통적으로 사용되는 속성을 제공한다
  • MixinsCRUD 중의 특정한 기능을 수행하는 메소드를 제공한다
  • GenericAPIViewMixins로 정확한 기능 구현이 어려울 때, 오버라이드를 통한 커스터마이징이 가능하다

GenericAPIView

< 기본 속성 >

  • 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 : 조회/수정/삭제

주요 Mixins

위의 mixin 상속 부분의 코드에서 쓰임을 확인할 수 있다.

  1. mixins.ListModelMixin
    • Queryset을 list화 한다
    • .list(request, *args, **kwargs) 메소드로 호출한다
    • GenericAPIViewself.fiter_queryset, self.get_queryset, self.get_serializer등의 메소드를 활용하여 데이터를 목록 형태로 return
    • 성공 시 200 OK

  1. mixins.CreateModelMixin
    • 모델 인스턴스를 생성하고 저장하는 기능을 제공
    • .create(request, *args, **kwargs) 메소드로 호출한다
    • 성공: 201 Created / 실패: 400 Bad Request

  1. mixins.RetrieveModelMixin
    • 존재하는 모델의 인스턴스를 리턴해준다
    • .retrieve(request, *args, **kwargs) 메소드로 호출하여 사용
    • 성공: 200 Created / 실패: 400 Bad Request

  1. mixins.UpdateModelMixin
    • 모델 인스턴스를 수정하여 저장해준다
    • .update(request, *args, **kwargs) 메소드로 호출하여 사용
    • 부분만 변경하고하 한다면 .partial_update(request, *args, **kwargs) 메소드를 호출하여야 하며 이 때, 요청은 HTTP PATCH requests이어야 한다
    • 성공: 200 OK / 실패: 404 Not Found

  1. mixins.DestoryModelMixin
    • 모델 인스턴스를 삭제한다
    • .destroy(request, *args, **kwargs) 메소드를 호출하여 사용
    • 성공: 204 No Content / 실패: 404 Not Found

이렇게 GenericAPIViewMixins의 관계까지 알아볼 수가 있었다.


generic으로 갈 수록 그 기능을 간편하게 정확히 쓸 수 있는 것 같고 API_View로 갈 수록 직접 짜야 하는 코드는 많아지지만 자유도가 높아지는 것 같다.

django에서 제공하는 것이 굉장이 많게 보이다 보니 정리하고 사용해 보는 데에 시간이 꽤 드는 느낌이다.

요령을 찾으려면 다양하게 많은 API를 만들어 봐야겠단 생각이 든다.

나머지 내용도 추가해서 정리해야겠다.


도움주신 분들
수학좌: [Django] APIView, Mixins, generics APIView, ViewSet을 알아보자
phyyou님: DRF 공부하기 (7) :: GenericAPIView와 Mixins
현암 코딩님: 4-2 Generic Views (Mixin,Concrete)

0개의 댓글