Udemy 강의를 들으면서, DRF 공식문서를 보면서, 그리고 구글링하면서 정리한 내용입니다.
Django REST Framework - Level One
The @api_view Decorator - Part One
DRF에서는 API 뷰에 쓸 수 있는 두 가지 wrapper를 제공한다. 하나는 @api_view
데코레이터로 함수 기반(function based) API 뷰를 짤 때 쓴다. 다른 하나는 APIView
클래스로, 클래스 기반(class based) API 뷰를 짤 때 사용한다. 이 wrapper를 사용하면 request instance를 받는 데에 필요한 모든 코드를 손쉽게 사용할 수 있으며, 상황에 맞는 response를 내보내주는 것, 그리고 예외처리까지 가능하다.
그렇다면 DRF를 이용해 짠 뷰는 메소드를 어떻게 구분해내는가? 요청한 request에 따라 결정되며 그것을 판별하는 장치가 @api_view
데코레이터다.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from news.models import Article
from news.api.serializers import ArticleSerializer
@api_view(['GET'])
def article_list_create_api_view(request):
# 데코레이터를 통해 request로 온 메소드와 일치하는지 판별 후,
if request.method == 'GET':
# 유효한(active = True) 기사 객체만 불러온다.
articles = Article.objects.filter(active = True)
# 직렬화한 데이터를 리턴하기 위해 위에서 선언한 query set을 serializer에 인자로 넣는다.
serializer = ArticleSerializer(articles)
return Response(serializer.data)
url을 설정한 뒤 서버를 띄워서 확인해보면 AttributeError가 뜬다. 왜일까? 에러 메시지를 확인해보자.
Got AttributeError when attempting to get a value for field `author` on serializer `ArticleSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'author'.
불러온 쿼리셋(list)을 단일 항목으로 직렬화하려고 해서 생기는 오류다. serializer =
부분에 many = True
를 추가해주면 된다.
serializer = ArticleSerializer(articles, many = True)
이제 모든 article list가 뜨는 것을 확인할 수 있다.
보다시피 DRF에서 API는 browsable하다. 기본적으로 웹 인터페이스를 가지고 있어서 개발자가 사용하기 매우 편리하다. 저 페이지 하나에서
- request 메소드
- 엔드포인트 주소
- status code 및 message
- 해당 api에서 사용 가능한 메소드
를 모두 확인할 수 있다. 장고를 쓸 때에는 Postman에서 엔드포인트 및 자료 리턴 형태를 테스트했지만 DRF에서는 웹 브라우저에서도 그 작업이 가능하다.
DRF의 편리한 점은 여기서 끝이 아니다! views.py 소스코드를 다시 들여다보면 장고를 쓸 때와는 어딘지 다른 점이 보인다. return
부분이다.
from rest_framework.response import Response
'''
코드 중략
'''
return Response(serializer.data)
꼬박꼬박 return JsonResponse
를 붙여줘야 json 형태로 리턴할 수 있었는데 DRF에서는 Response
클래스만 사용하면 된다. request 맥락에 따라서 DRF가 알아서 가장 적절한 형태로 response를 보내기 때문이다! DRF 만세! 박수~~
이제 post 메소드를 추가해보자.
'''
코드 전략
'''
@api_view(['GET', 'POST'])
def article_list_create_api_view(request):
if request.method == 'GET':
articles = Article.objects.filter(active = True)
serializer = ArticleSerializer(articles, many = True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = ArticleSerializer(data = request.data)
# 장고와 달리 DRF에서는 request에서 데이터를 받을 때(request.data)
# 반드시 .is_valid() 여부를 체크해야 한다.
# valid하지 않을 때는 serializer.errors를 리턴한다.
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status = status.HTTP_201_CREATED)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)
Allow 메소드에 POST가 추가되었으며 하단에 객체를 생성할 수 있는 폼이 생겼다. 폼에 json 형태로 작성해서 post 버튼을 누르면 새 인스턴스를 간편하게 추가할 수 있다. "id"
와 "created_at"
, "updated_at"
항목은 자동생성이므로 작성하지 않아도 괜찮다.
post 클릭 후 하단 폼을 이용해 추가한 인스턴스를 확인할 수 있다.
The @api_view Decorator - Part Two
이제 개별 인스턴스를 조회(get), 수정(put), 삭제(delete)할 수 있는 함수를 만들어보자.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from news.models import Article
from news.api.serializers import ArticleSerializer
@api_view(['GET', 'POST'])
def article_list_create_api_view(request):
'''
코드 중략
'''
@api_view(['GET', 'PUT', 'DELETE'])
def article_detail_api_view(request, pk):
# try, except 대신 get_object_or_404를 import 해서 쓸 수도 있다.
try:
# pk(인스턴스의 id)값을 받아 어떤 인스턴스인지 특정
# url slug로 pk값을 받도록 urls.py에서 설정해준다.
article = Article.objects.get(pk = pk)
# 받은 pk값으로 조회했을 때 해당하는 인스턴스가 없다면 출력할 에러 코드와 메시지를 설정한다.
except Article.DoesNotExist:
return Response({'error' : {
'code' : 404,
'message' : "Article not found!"
}}, status = status.HTTP_404_NOT_FOUND)
# 만약 article이 존재한다면,
if request.method == 'GET':
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ArticleSerializer(article, data = request.data)
# request에서 data를 받았으니 .is_valid() 필수
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
article.delete()
# 인스턴스를 삭제한 뒤에는 204 NO CONTENT를 리턴
return Response(status = status.HTTP_204_NO_CONTENT)
url slug에 불러오고 싶은 인스턴스의 id 필드 값(value)에 해당하는 숫자를 입력해주면 조회할 수 있다. 수정, 삭제도 가능하다.
없는 id를 입력하면 뷰에서 선언한 not found 메시지가 뜬다.