이 포스트는 인프런의 "Django REST Framework 핵심사항"강의를 듣고 제작하였습니다.
이번 글에서는 저번 글에서 다뤘던 개념에서 더 나아가 코드를 직접 보면서 GenericView들 중 일부에 대해 어떻게 활용하는 지에 대해서 간단하게 다뤄보려고 한다.
본격적인 시작전에 우선 우리가 사용할 URL과 그 URL에 사용할 View를 생각을 해놔야 한다.
일단 우리가 만들 웹사이트는 블로그의 형식이고 이에 따라서 글의 리스트페이지, 글의 상세페이지, 댓글 작성 등을 할 수 있어야된다.
View의 이름에서 알 수 있듯이 queryset의 리스트를 보여주는 것이 ListAPIView이고 그 중 하나의 인스턴스에 대해서만 보여줄 때 사용하는 것이 RetrieveAPIView이다.
이에 대해서 한번 알아보도록 하자.
우선은 글의 리스트를 보여주는 페이지와 글의 상세페이지를 보여주는 페이지를 구현하기 위해서는 각각의 URL들에 대해서 GenericView들을 매칭시켜주어야 한다.
다음과 같이 urls.py에서 각각의 URL과 그에 따른 View를 작성해주자.
#api2 > urls.py
from django.urls import path, include
from api2 import views
urlpatterns = [
path('post/',views.PostListAPIView.as_view(),name='post-list'),
path('post/<int:pk>',views.PostRetrieveAPIView.as_view(),name='post-detail'),
]
as_view()메소드에 대한 설명은 이 글에 자세히 나와있으니 자세한 설명은 생략한다.
쉽게 말해서 Class형 View를 사용하기 위한 진입메소드라고 생각하면 된다.
DRF에서는 url-name과 관련된 기본 규칙이 있다.
꼭 아래의 규칙을 지킬 필요는 없지만 url-name은 해당 url의 동작을 유추할 수 있는 단어로 짓는 것이 좋다. ViewSet + router의 조합을 사용한다면 자동적으로 아래의 규칙에 따라 url-name이 정의되고 동작 시 사용된다.
DRF의 url-name 규칙
{ model 명 }-list:pk가 없는 기본urlex ) post/
{ model 명 }-detail:pk를 사용하는urlex ) post/1/
바로 코드부터 확인해보도록 하자.
#api2 > views.py
from rest_framework.generics import ListAPIView, RetrieveAPIView, CreateAPIView
from api2.serializers import PostSerializer, CommentSerializer
from blog.models import Post, Comment
class PostListAPIView(ListAPIView):
queryset=Post.objects.all()
serializer_class=PostSerializer
class PostRetrieveAPIView(RetrieveAPIView):
queryset=Post.objects.all()
serializer_class=PostSerializer
urls.py에서 각각 url과 대응시킨 view들에 대한 코드들이다. 매우 간결한 것을 알 수 있는데 이것이 바로 GenericView의 장점이라고 할 수 있을 것이다. 기본 제공하는 기능만 사용한다면 이렇게만 작성해도 완성이다.
각각 ListAPIView와 RetrieveAPIView를 상속받아 만들어졌으며 queryset과 serilalizer_class가 정의
된 것을 확인할 수 있다. 이 두 개에 대한 정보는 반드시 있어야된다.
GenericView의 간단한 동작흐름을 살펴보면 다음과 같다.
db로부터데이터들을 가져옴 -> 그데이터를serializer->serializer된데이터를 반환
즉, queryset = db로부터 가져온 data들, serializer_class = serializer 방법 / 형식으로 이해하면 된다.
코드를 살펴보기전에 간략하게 ListAPIView와 RetrieveAPIView에 대해서 알아보려고 한다.
저번 글에서 소개했던 것과 같이 cdrf.com에 DRF에 존재하는 클래스들의 정보들이 잘 정리되어있어 이를 활용해도 된다. 이 글에서는 파일을 직접 접근해서 ListAPIView와 RetrieveAPIView의 차이점에 대해서 알아보려고 한다.
#rest_framework > mixins.py
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
#페이지에 관한 부분
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
#rest_framework > mixins.py
class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
각각의 Mixin클래스에서 list()와 retrieve()메소드들을 확인할 수 있다.
이를 통해 알 수 있는 차이점은 다음과 같다.
list와 retrieve의 차이점
list( ) retrieve( ) queryset을 가져오는 메소드 filter_queryset get_object serializer의 인자 many = True Default ( many = False )
queryset:list는queryset전체에 대해서 가져오고retrieve는queryset의인스턴스 하나에 대해서만 가져온다.
get_serializer:list는many=True로 지정하여queryset전체의serilaizer된 값을 반환하지만
retrieve는하나의 인스턴스에 대해서만serializer된 데이터를 반환한다.
get_object와get_serlializer의 동작방식에 대해서는GenericAPIView에서 찾아볼 수 있다.
이러한 차이점으로 인해 ListAPIView와 RetrieveAPIView이 각각 다른 동작을 하는 것이라고 생각하면 된다.
serializer에 관한 이야기가 많이 나오고 있는데 쉽게 생각하면 사용자에게 정보를 보여줄 출력포맷을 정하는 것으로 이해할 수 있다.
코드와 함께 살펴보자.
#api2 > serializers.py
from rest_framework import serializers
from blog.models import Post, Comment
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'image', 'like', 'category']
PostSerializer는 ModelSerializer클래스를 상속받아 만들어졌으며 클래스의 내용은 일단 Meta클래스로 정의했다. Serializer는 상황에 따라 더욱 세분화하여 나타낼 수도 있으며 이에 대해서는 다음에 자세히 다뤄보도록 하겠다.
이렇게 urls.py, views.py, serializers.py를 정의하고 나면 대략적인 구현이 끝났다고 보면 된다. 이제 서버를 실행시켜보도록 하자.
이제 서버를 실행해서 우리가 작성했던 구현했던 API에 대한 URL에 접속해서 살펴보자.
사진과 같이
post/ 접근하면 이렇게 DB에 저장되었던 post에 대한 정보가 리스트로 잘 출력되는 것을 알 수 있다.
그리고
post/1/로 접근하면 이렇게 id와 pk가 서로 같은 하나의 인스턴스가 출력되는 것을 확인할 수 있다.
ListAPIView는queryset에 대한리스트를,RetrieveAPIView는 그 중하나의 인스턴스를 사용자에게 반환한다.
- 이 두
GenericView가 다르게 동작하는 이유는 상속받은Mixin클래스에서의 동작방식이 다르기 때문이다.