파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 강의를 듣고 정리한 글입니다.
ViewSet은 REST의 반복적인 코딩 패턴을 줄여준다.
우리는 일반적으로 REST API를 구현할 때 모델을 기준으로 List와 Detail URL에 대한 API를 구현한다.
이 때 List API는 GET, POST 메소드를 구현하며 Detail API 는 GET, PUT, DELETE메소드를 구현한다.
이 각 2개의 URL별로 모두 5개의 메소드 구현을 REST의 일관된 패턴으로 볼 수도 있을 것이다.
DRF에서는 모델을 기준으로 하나의 ViewSet으로 묶어서 위에서 언급한 패턴을 한방에 구현할 수 있다. ViewSet에 queryset과 serializer를 지정만 해주고 Router클래스로 url에 추가만 해주면 된다.
단일 리소스에서 관련있는 View들을 단일 클래스에서 제공
list/create
, detail/update/partial_update/delete
와 같이 두개의 클래스 기반뷰가 필요하다.# https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/
from rest_framework import viewsets
from rest_framework.response import Response
class PostViewSet(viewsets.ViewSet):
def list(self, request): # list
queryset = Post.objects.all()
serializer = PostSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk): # detail
queryset = Post.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = PostSerializer(user)
return Response(serializer.data)
# https://www.django-rest-framework.org/api-guide/routers/
router = DefaultRouter()
router.register('post', PostViewSet, basename='post') # 2개의 URL
router.urls
# urls에 다음과 같이 추가한다. -> path('', include(router.urls)
아래의 코드 역시 정형화된 패턴
⇒ ModelViewSet을 통해 간결하게 구현하실 수 있습니다.
from rest_framework import generics
class PostListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
2가지 ModelViewSet
viewsets.ReadOnlyModelViewSet
viewsets.ModelViewSet
from rest_framework import viewsets
class PostViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# PostViewSet으로부터 필요한것만 뽑아 쓰기
post_list = PostViewSet.as_view({
'get': 'list',
})
post_detail = PostViewSet.as_view({
'get': 'retrieve',
})
# urls에 직접 path를 다 써야한다.
# Router를 통해 일괄등록을 하면 추가 기능이 있다. http://localhost:8000/post/1.api, http://localhost:8000/post/1.json 등의 포맷 인자(api, json)를 추가로 url_patterns에 추가된다. 포맷 인자로 요청 시 요청에 따라 json, api응답이 온다. 아래 Router 참조
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('post', views.PostViewSet) # 'post': URL에 사용될 리소스 이름이다. 보통은 모델명 사용
urlpatterns = [
path('', include(router.urls)),
]
ReadOnlyModelViewSet과 ModelViewSet에 대해서 동일한 URLPattern 리스트가 생성
- list route
- detail route
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('post', views.PostViewSet)
urlpatterns = [
path('', include(**router.urls**)),
]
# ...
[
<URLPattern '^post/$' [name='post-list']>,
<URLPattern '^post\.(?P<format>[a-z0-9]+)/?$' [name='post-list']>, # 주의) 구분자 "_"가 아니라 "-"이다.
<URLPattern '^post/(?P<pk>[^/.]+)/$' [name='post-detail']>,
<URLPattern '^post/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='post-detail']>,
<URLPattern '^$' [name='api-root']>,
<URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>
# api-root : 현 Router에 등록된 ViewSet내역을 조회
]
from rest_framework.decorators import action
class PostModelViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
"""
URL Reverse 명: basename-함수명 (언더바는 하이픈으로 변경)
즉, post-public이 된다.
"""
@action(detail=False, methods=['GET']) # 특정 포스팅이 아니므로 detial=False
def public(self, request):
qs = self.get_queryset().filter(is_public=True)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
@action(detail=True, methods=['PATCH']) # 특정 포스팅이므로 detail=True
def set_public(self, request, pk): # public으로 변경하는 api
instance = self.get_object()
instance.is_public = True
instance.save() # 또는 instance.save(update_fields=['is_public'])
serializer = self.get_serializer(instance)
return Response(serializer.data)
# @action의 detail인자에 따라서 list URL을 탈건지, detail URL을 탈건지 결정된다.
[
# ...
<URLPattern '^post/public/$' [name='post-public']>,
<URLPattern '^post/public\.(?P<format>[a-z0-9]+)/?$' [name='post-public']>,
# ...
<URLPattern '^post/(?P<pk>[^/.]+)/set_public/$' [name='post-set-public']>,
<URLPattern '^post/(?P<pk>[^/.]+)/set_public\.(?P<format>[a-z0-9]+)/?$' [name='post-set-public']>,
# ...
]
http PATCH http://도메인/post/10/set_public/
http PATCH http://도메인/post/10/ is_public=true # PATCH요청 시 ModelViewSet에 구현되어 있는 partial_update로직을 통해서 업데이트 된다.