DRF - ViewSet

20289 단어 djangodjango

ViewSet

  • 여러 가지 API의 기능을 통합해서 하나의 API Set로 제공
  • 하나의 모델을 가지고 list, detail 등 각각의 API를 만들어 보면, 중복되는 로직이 많다. 이런 경우, ViewSet을 쓰게 되면 중복되는 로직의 코드를 줄일 수 있어 코드의 효율성을 높일 수 있다.
  • ViewSets은 get, post 같은 메소드 핸들러를 제공하지는 않지만, list, create 같은 액션을 제공한다.
  • ViewSet을 위한 method 핸들러들은 as_view()함수를 사용해 view가 끝나는 시점에 해당하는 행동을 취할 때 바인딩한다.
  • 일반적으로 url 설정 내에서 viewset들의 view를 명시적으로 등록하는 것보다 router 클래스로 viewset을 등록하는 것이 좋음.

Code

이전 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")
]

결과




참고하면 좋은 사이트

좋은 웹페이지 즐겨찾기