DRF - ViewSet
ViewSet
- 여러 가지 API의 기능을 통합해서 하나의 API Set로 제공
- 하나의 모델을 가지고 list, detail 등 각각의 API를 만들어 보면, 중복되는 로직이 많다. 이런 경우, ViewSet을 쓰게 되면 중복되는 로직의 코드를 줄일 수 있어 코드의 효율성을 높일 수 있다.
- ViewSets은
get, post 같은 메소드 핸들러를 제공하지는 않지만, list, create 같은 액션을 제공한다.
- ViewSet을 위한 method 핸들러들은 as_view()함수를 사용해 view가 끝나는 시점에 해당하는 행동을 취할 때 바인딩한다.
- 일반적으로 url 설정 내에서 viewset들의 view를 명시적으로 등록하는 것보다 router 클래스로 viewset을 등록하는 것이 좋음.
Code
get, post 같은 메소드 핸들러를 제공하지는 않지만, list, create 같은 액션을 제공한다.이전 DRF의 Auth 부분 코드를 이어 수정하여 테스트할 것이다.
ViewSet
profiles.api.views.py
변경 전
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from profiles.models import Profile
from profiles.api.serializers import ProfileSerializer
class ProfileList(generics.ListAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = [IsAuthenticated]
변경 후
from rest_framework.viewsets import ReadOnlyModelViewSet
class ProfileViewSet(ReadOnlyModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = [IsAuthenticated]
profiles.api.urls.py
변경 전
from django.urls import path
from profiles.api.views import ProfileList
urlpatterns = [
path("profiles/", ProfileList.as_view(), name="profile-list")
]
변경 후
- viewset의 경로를 일일이 지정해주었다.
from django.urls import path
from profiles.api.views import ProfileViewSet
profile_list = ProfileViewSet.as_view({"get": "list"})
profile_detail = ProfileViewSet.as_view({"get": "retrieve"})
urlpatterns = [
path("profiles/", profile_list, name="profile-list"),
path("profiles/<int:pk>/", profile_detail, name="profile-detail")
]
ViewSet + Router
profiles.api.urls.py
router를 이용하면 경로 지정을 DRF에서 자동화해 주기에 통상 viewset의 url은 router로 설정해준다.
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from profiles.api.views import ProfileViewSet
# router는 자동으로 link들을 생성해낸다.
router = DefaultRouter()
router.register(r"profiles", ProfileViewSet)
urlpatterns = [
path("", include(router.urls))
]
테스트 결과
- list

- get

- root

router.register() method
router.register(r"status", ProfileStatusViewSet, basename="status")
필수 argument
- prefix : url에 들어갈 자원의 이름(route의 집합들을 사용하기 위한 URL prefix)
- viewset : viewset class
옵션 argument
- basename : django에서 사용하는 path name처럼 view에 이름을 지정
- 공란으로 남겨두면 viewset을 참고하여 자동으로 생성한다. 그래서 viewset에 무조건 queryset이 지정되어 있어야한다.
- 만약 viewset에 queryset attribute를 포함하지 않는다면, 사용자는 viewset을 등록할 때, base_name을 지정해야 한다.
ViewSet + update 기능 추가
profiles.api.views.py
from rest_framework import viewsets
from rest_framework import mixins
from profiles.api.permissions import IsOwnProfileOrReadOnly
class ProfileViewSet(mixins.UpdateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = [IsAuthenticated, IsOwnProfileOrReadOnly]
profiles.api.permissions.py
from rest_framework import permissions
class IsOwnProfileOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.user == request.user
결과
- 권한 있을 때(소유자)

- 권한 없을 때

ViewSet + update&delete 기능 추가
profiles.api.views.py
from rest_framework.viewsets import ModelViewSet
from profiles.models import ProfileStatus
from profiles.api.serializers import ProfileStatusSerializer
from profiles.api.permissions import IsOwnerOrReadOnly
class ProfileStatusViewSet(ModelViewSet):
queryset = ProfileStatus.objects.all()
serializer_class = ProfileStatusSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
def perform_create(self, serializer):
user_profile = self.request.user.profile
serializer.save(user_profile=user_profile)
profiles.api.permissions.py
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.user_profile == request.user.profile
profiles.api.urls.py
from profiles.api.views import ProfileViewSet, ProfileStatusViewSet
# 추가
router.register(r"status", ProfileStatusViewSet)
결과
- Api Root

- api/status/

- post

- api/status/

- api/status/1/

AvatarUpdateView 추가
profiles.api.views.py
from profiles.api.serializers import ProfileAvatarSerializer
class AvatarUpdateView(generics.UpdateAPIView):
serializer_class = ProfileAvatarSerializer
permission_classes = [IsAuthenticated]
def get_object(self):
profile_object = self.request.user.profile
return profile_object
profiles.api.urls.py
from profiles.api.views import ..., AvatarUpdateView
urlpatterns = [
...,
path("avatar/", AvatarUpdateView.as_view(), name="avatar-update")
]
결과

참고하면 좋은 사이트
Author And Source
이 문제에 관하여(DRF - ViewSet), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@nameunzz/DRF-ViewSet저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)