APIView와 @api_view
DRF의 2가지 기본 뷰
- APIView : 클래스 기반 뷰
- @api_view : 함수 기반 뷰를 위한 장식자
APIView
APIView를 조금 더 표준화 시킨것이 Generic
Generic을 합쳐 놓은 것이 Viewset이다.
- APIView와 Generic 까지는 하나의 클래스는 하나의 URL로만 매핑된다.
- Viewset은 두개의 URL에 대한 처리를 하나의 클래스에서 처리해준다.
- 예를 들면 Generic까지는 포스트의 수정, 삭제, 생성 등 중에 1개만 구현
- Viewset은 다수를 엮어서 구현
하나의 CBV이므로 -> 하나의 URL만 처리 가능
각 method(get, post, put, delete)에 맞게 멤버함수를 구현하면, 해당 method 요청이 들어올 때 호출
- 직렬화/비직렬화 처리 (JSON 등)
- 인증 체크
- 사용자 제한 체크 : 호출 허용량 범위인지 체크
- 권한 클래스 지정 : 비인증/인증 유저에 대해 해당 API 호출을 허용할 것인지를 결정
- 요청된 API 버전 문자열을 탐지하여, request.version에 저장
APIView 내 dispatch
- dispatch 함수에서 initial 함수가 있다.
- 들어가서 보면 포멧, 컨텐트 탐지, 버전 탐지
- 그리고 마지막에 인증 수행, 퍼미션 체크, 요청 수 제한을 체크한다.
- 다 이루어지고 난다면 다시 dispatch 함수에서 initial 밑 부분으로 와서 request.method를 보는데 mothod는 전부 대문자이다 (예 : POST, GET, DELETE, PUT)
- 그렇기 때문에 소문자로 바꾸어 주고, 현재 해당 멤버 함수를 제공해주면 그 멤버함수를 찾아서 호출해준다. (handler가 호출 해주는 함수이다)
- 그리고 return 값으로 응답을 받고, 리턴해준다.
클래스 형태) APIView 구현 샘플 (list/create)
from rest_framework.response import Response
from rest_fraework.views import APIView
from .models import Post
from .serializers import PostSerializer
class PostListAPIView(APIView):
def get(self, request):
qs = Post.objects.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)
from django.views.decorators.csrf import csrf_exempt
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
return csrf_exempt(view)
- view가 csrf_exempt 장식자로 이미 감싸져 있기에 POST 요청에서 csrf token 체크를 하지 않는다.
@csrf_exempt
def post_list(request):
pass
def post_list(request):
pass
post_list = csrf_exempt(post_list)
- 위의 두 코드는 같은 코드이다.
- 이것이 파이썬의 장식자 문법이다.
- 그래서 가장 위의 코드 return csrf_exepmt(view)는 as_view 함수를 호출 했을 때 csrf_exempt(view) 장식자가 씌워져서 나오는 것이다.
클래스 형태) APIView 구현 샘플 (detail/update/delete)
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Post
from .serializers import PostSerializer
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)
직접 구현을 했지만..
- 우리는 APIView 내의 dispatch 함수에서 initial 함수가 하는 역할인 인증, 퍼미션, 요청 수 제한 등 아무것도 하지 않고, 데이터를 받아서 db에 저장해주거나, db에 있는 것들을 가져와서 응답해주기, 상황에 맞는 응답 코드 반응해주기 밖에 하지 않았다.
- 그래서 이러한 것들을 구현하다 보면 공통되는 패턴이 보인다.
- 이러한 패턴들을 모아 구현이 되어 있는 것이 drf의 generics이다.
generics의 함수
generics.ListAPIView
generics.CreateAPIView
generics.ListCreateAPIView
- ListAPIView와 CreateAPIView를 합친 것이 LISTCreateAPIView이다.
generics.DestroyAPIView
generics.UpdateAPIView
- 등등 엄청나게 많다.
- 여튼 위에서 직접 구현한 총 5개의 함수들을 패턴화 시켜 놓은 것이 drf의 generics이다.
- 그리고 이러한 generics를 합쳐서 구조화 시켜 놓은 것이 Viewset이다.
함수 형태) @api_view 장식자 구현 샘플 (list/create)
from django.http import get_object_or_404
from rest_framework import status, Response
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':
serializer = PostSerializer(Post.objects.all(), 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.errors, status=400)
- 하나의 작업 만을 구현코자 할 때 @api_view를 쓰면 편하다.
- CBV에서는 def get, def post 이런식으로 구현하면 되는데, FBV에서는 어떤 메소드를 지원하겠다고 list로 꼭 써서 데코레이터로 씌워야한다.
- 그래서 특정 메소드가 왔을 때, post_list라는 함수가 호출이 되고, if 조건으로 메소드를 통해서 분기처리를 해야 한다.
함수 형태) @api_view 장식자 구현 샘플 (detail/update/delete)
from rest_framework.decorators import api_view
@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':
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)