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.)