Backend/Django
[django-rest-framework] (7) ViewSet / ModelViewSet
dltjdn
2022. 1. 22. 04:28
ViewSet
list/create → 하나의 url 필요
detail/update/partial_update/delete → 하나의 url 필요
》 최소 2개의 클래스 기반 뷰 필요
ViewSet은 2개의 url에 대한 구현을 하나의 클래스에서 지원해줌
# views.py
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework.response import Response
class PostViewSet(viewsets.ViewSet):
def list(self, request):
qs = Post.objects.all()
serializer = PostSerializer(qs, many=True)
return Response(serializer.data)
def detail(self, request, pk):
post = get_object_or_404(Post, pk=pk)
serializer = PostSerializer(post)
return Response(serializer.data)
# urls.py
router = DefaultRouter()
router.register('post', PostViewSet, basename='post') # 최소 2개의 url에 대해서 url 패턴 생김
urlpatterns = [
path('', include(router.urls)) # router.urls는 url 패턴 리스트
]
ModelViewSet
viewsets.ReadOnlyModelViewSet : get 요청에만 반응하는 ModelViewSet
- list 지원 → 1개의 url
- detail 지원 → 1개의 url
viewsets.ModelViewSet
- list/create 지원 → 1개의 url
- detail/ update/ partial_update/ delete 지원 → 1개의 url
URL Patterns에 매핑하기
router = DefaultRouter()
router.register('post', PostViewSet, basename='post')
urlpatterns = [
path('', include(router.urls)) # router.urls는 url 패턴 리스트
]
'post' 는 prefix 주소 → namespace 역할으로 각각의 api를 구별하게 한다
Router
ReadOnlyModelViewSet과 ModelViewSet에 대해서 동일한 URLPatttern 리스트가 생성된다
format ( ex. .json, .api) 지원
[
# list router
<URLPattern '^post/$' [name='post-list']>,
<URLPattern '^post\.(?P<format[a-z0-9]+)/?$' [name='post-list']>,
# detail router
<URLPattern '^post/(?P<pk>[^/.]+)/$' [name='post-detail']>,
<URLPattern '^post/(?P<pk>[^/.]+)\.(?P<format[a-z0-9]+)/?$' [name='post-detail']>,
# 현 Router에 등록된 ViewSet 내역을 조회
<URLPattern '^$' [name='api-root']>,
<URLPattern '^\.(?P<format[a-z0-9]+)/?$' [name='api-root']>,
]
ViewSet에 새로운 EndPoint 추가
from rest_framework.decorators import action
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
#list.url
@action(detail=False, method=['GET'])
def public(self,request):
qs = self.get_queryset().filter(is_public=True)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
# detail.url
@action(detail=True, method=['PATCH'])
def set_public(self, request, pk):
instance = self.get_object()
instance.is_public = True
instance.save(update_fields=['is_public'])
serializer = self.get_serializer(instance)
return Response(serializer.data)
[
<URLPattern '^post/public/$' [name='post-public']>,
<URLPattern '^post/public\.(?P<format>[a-z0-9]+)/?/$' [name='post-public']>,
<URLPattern '^post/(?P<pk>[^/.]+)/set_public/$' [name='set-public']>,
<URLPattern '^post/(?P<pk>[^/.]+)/set_public\.(?P<format>[a-z0-9]+)/?/$' [name='set-public']>,
]
※ URL Revers 명은 basename-함수명 or basename_함수명
HTTPie 활용해 요청
>> http PATCH http://도메인/post/10/set_public/
>> http PATCH http://도메인/post/10/ is_public=true
- 두가지는 동일한 결과 나온다 →10번째 게시물의 is_public 이 true로 바뀜
- 두 번째는 set_pubic, public 사용하지 않고, 기본 ViewSet에서 지원해주는 PATCH method의 partial_update라는 멤버함수 호출 → @action 구현이 없고 단순히 ModelViewSet만 있을 때 이 방법 사용