Backend/Django

[django-rest-framework] (5) APIView / @api_view

dltjdn 2022. 1. 21. 16:30

DRF의 2가지 기본 뷰

APIView : 클래스 기반 뷰

@api_view : 함수 기반 뷰를 위한 장식자 

 

두 가지 다 View에 여러 기본 속성 부여

1. renderer_classes : 직렬화 class 다수

  • rest_framework.renderers.JSONRenderer : JSON직렬화
  • rest_framework.renderers.TemplateHTMLRenderer : HTML 페이지 직렬화

2. parser_classes : 비 직렬화 class 다수

  • rest_framework.parsers.JSONParser : JSON 포맷 처리
  • rest_framework.parsers.FormParser
  • rest_framework.parsers.MultiPartParser

3. authentication_classes : 인증 class 다수

  • rest_framework.authentication.SessionAuthentication : 세션에 기반한 인증
  • rest_framework.authentication.BasicAuthentication : HTTP Basic 인증

4. throttle_classes : 사용량 제한 class 다수

  • 빈 튜플

5. permission_classes : 권한 class 다수

  • rest_framework.permissions.AllowAny : 누구라도 접근 허용

6. content_negotiation_class : 요청에 따라 적절한 직렬화/비 직렬화 class를 선택하는 class

  • rest_framework.negotiation.DefaultContentNegotiation : 같은 URL로의 요청이지만, JSON 응답을 요구하는 것인지 / HTML 응답을 요구하는 것인지 판단

7. metadata_class : 메타 정보를 처리하는 class

  • rest_framework.metadata.SimpleMetadata

8. versioning_class : 요청에서 API버전 정보를 탐지하는 class

  • None : API 버전 정보를 탐지하지 않겠다
  • 요청 URL에서, GET인자에서, HEADER에서 버전정보를 탐지하여 해당 버전의 API뷰가 호출되도록 한다

 

 

APIView

하나의 CBV 이므로 하나의 URL만 처리 가능

 

1. initial (초기화) 단계

  • 직렬화/비직렬화 처리 (JSON 등)
  • 인증 체크
  • 사용량 제한 체크 : 호출 허용량 범위인지 체크
  • 권한 클래스 지정 : 비인증/인증 유저에 대해 해당 API 호출을 허용할 것인지를 결정
  • 요청된 API 버전 문자열을 탐지하여, request.version에 저장

2. 요청이 들어올 때 미리 구현해둔 method(get, post, put, delete)에 맞게 멤버 함수를 호출

 

APIView 구현 예시

(1) 클래스 형태

from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
from .models import Post
from .serializers import PostSerializer

class PostListAPIView(APIView):
    def get(self, request):
        qs = Post.obejcts.all()
        serializer = PostSerializer(qs, 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 )

class PostDetailAPIView(APIView):
    def get(self, request, pk, format=None):
        post = get_object_or_404(Post, pk=pk)
        serializer = PostSerializer(post)
        return Response(serializer.data)

    def put(self, request, pk):
        post = get_object_or_404(Post, pk=pk)
        # Form 생성자의 첫번째 인자는 data이지만, Serializer 생성자의 첫번째 인자는 instance이다
        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 = get_object_or_404(Post, pk=pk)
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

 

(2) 함수 형태

from django.shortcuts import get_object_or_404
from rest_framework import Response, status
from rest_framework.decorators import api_view
from .models import Post
from .serializers import PostSerializer

@api_view(['GET', 'POST'])
def post_list(request):
    if request.method == 'GET':
        qs = Post.objects.all()
        serializer = PostSerializer(qs, many = True)
        return Response(serializer.data)

    else : 
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.error, status=400)

@api_view(['GET', 'PUT', 'DELETE'])
def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)

    if request.method == 'GET':
        serializer = PostSerializer(post)
        return Response(serializer.data)

    elif request.method == 'PUT':
     	# Form 생성자의 첫번째 인자는 data이지만, Serializer 생성자의 첫번째 인자는 instance이다
        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)
      
    else:
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

 

 

※ 하나의 작업만을 구현하고자 한다면 @api_view를 쓰는 것이 더 편리!

class PublicPostListAPIView(APIView):
    def get(self, request):
        qs = Post.objects.filter(is_public=True)
        serializer = PostSerializer(qs, many=True)
        return Response(serializer.data)

@api_view(['GET'])
def public_post_list(request):
     qs = Post.objects.filter(is_public=True)
     serializer = PostSerializer(qs, many=True)
     return Response(serializer.data)

 

 

c.f) 위와 같은 코드를 generics를 이용해서 구현해볼 수도 있다

class PublicPostListAPIView(generics.ListAPIView):
    queryset = Post.objects.filter(is_public=True)
    serializer_class = PostSerializer

APIView를 좀 더 표준화한 것이 Generic 이고 이것을 좀 더 표준화 한 것이 ViewSet이다

  • APIView와 Generic :  하나의 클래스가 하나의 URL에 매핑
  • ViewSet :  하나의 클래스가 두 개의 URL에 대한 처리