DRF 12 - ViewSet

Joey Lee·2020년 7월 13일
4

Django REST Framework

목록 보기
13/16

1. ViewSet이란?

Django REST framework는 단일 클래스에 관련 있는 view들을 결합한 ViewSet 기능을 제공한다. 즉, ViewSet는 여러 가지 API의 기능을 통합해서 하나의 API Set로 제공하는 것이다.

하나의 모델을 가지고 list, detail 등 각각의 API를 만들어 보면, 중복되는 로직이 많다. 이런 경우, ViewSet을 쓰게 되면 중복되는 로직의 코드를 줄일 수 있어 코드의 효율성을 높일 수 있다.

  • ViewSets은 .get(), .post() 같은 메소드 핸들러를 제공하지는 않지만, .list(), .create() 같은 액션을 제공함
  • ViewSet을 위한 method 핸들러들은 as_view()함수를 사용해 view가 끝나는 시점에 해당하는 행동을 취할 때 바인딩함
  • 일반적으로 url 설정 내에서 viewset들의 view를 명시적으로 등록하는 것보다 router 클래스로 viewset을 등록하는 것이 좋음.

2. ProfileViewSet 작성 (List/Detail)

기존에 사용자 Profile 정보를 List로 보여주는 API가 있었다.
이 코드를 list뿐만 아니라 detail도 처리할 수 있는 ViewSet API로 아래와 같이 수정하여 만들 수 있다.

1) views.py 수정

[변경 전]

class  ProfileListView(generics.ListView):
    queryset = Profile.objects.all()
    serializer_class = ProfileStatusSerializer
    permission_classes = [IsAuthenticated]

[변경 후]

from rest_framework.viewsets import ReadOnlyModelViewSet   # ViewSet 임포트


class  ProfileViewSet(ReadOnlyViewSet):
    queryset = Profile.objects.all()
    serializer_class = ProfileStatusSerializer
    permission_classes = [IsAuthenticated]

2) urls.py 수정

#from profiles.api.views import ProfileList
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-list'),
]

3) 테스트 결과 확인

  • list view 확인
  • detail view 확인

4) Router 설정

앞에서는 urls.py에서 viewset의 경로를 직접 다 지정해 주었다. 하지만 router를 이용하면 경로 지정을 DRF에서 자동화해 주기에 통상 viewset의 url은 router로 설정을 해 준다.

이를 위해 아래와 같이 urls.py 파일을 수정한다.

[urls.py]

from django.urls import include, path
from profiles.api.views import ProfileViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"profiles", ProfileViewSet)

urlpatterns = [
    path("", include(router.urls))
]

그리고 Django REST API에서 확인을 해 보면 아래와 같이 동일한 결과가 출력됨을 확인할 수 있다. 다만, API 명칭이 변경되어서 Profile List, Profile Instance로 출력됨을 알 수 있다.

그리고 현재까지는 허용하는 메소드가 GET임을 알 수 있다.
[결과 보기]

3. ProfileViewSet 재작성 (List/Detail/Update)

1) 코드 작성

위의 ProfileViewSet은 List, Detail 기능을 수행하는 View 클래스였다. 이 클래스를 update까지 할 수 있는 ViewSet으로 재작성해 보자.

Profile은 작성한 본인만 수정할 수 있어야 함으로 먼저 permissions.py에서 Owner가 아니면 읽기만 가능한 IsOwnerOrReadOnly 클래스를 만든다.

[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

그 다음 수정까지 되도록 views.py를 수정하자.
앞에서는 ReadOnlyViewSet 클래스를 상속 받았지만, 이번에는 딱 우리는 원하는 ViewSet이 없음으로 필요한 기능들만 Mixins으로 상속 받아서 사용한다. 이를 위해 viewsetsmixins을 상속 받는다.

그리고 permission_classes에 IsOwnerOrReadOnly 를 추가한다.

[views.py]

from rest_framework import viewsets
from rest_framework import mixins

class  ProfileViewSet(mixins.UpdateModelMixin, 
                        mixins.ListModelMixin, 
                        mixins.RetrieveModelMixin,
                        viewsets.GenericViewSet):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer
    permission_classes = [IsAuthenticated, IsOwnProfileOrReadOnly]

2) 테스트 결과 확인

API 호출 결과를 보면 PUT, PATCH가 추가되었음을 확인할 수 있다.
pk 2번 프로파일은 내가 작성한게 아니므로 PUT 옵션이 뜨지 않는다.
pk1 번은 본인이 작성한 프로파일임으로 수정할 수 있도록 옵션이 나온다.

4. ProfileStatusViewSet 작성

ProfileStatusViewSet은 profile status를 Create, Read, Update, Detail를 다 할 수 있는 ViewSet으로 만드려고 한다. 이를 위해 ProfileStatusViewSetModelViewSet을 상속 받아서 작성한다.

ModelViewSet에 대한 설명 필요!

[views.py]

from rest_framework.viewsets import ModelViewSet

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)

urls.py에 아래 내용을 추가한다.

[urls.py]

from profiles.api.views import ProfileStatusViewSet

router = DefaultRouter()
#router.register(r"profiles", ProfileViewSet)
router.register(r"status", ProfileStatusViewSet)

5. AvatarUpdateView 작성

1) 코드 작성

[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

[urls.py]

from profiles.api.views import AvatarUpdateView

urlpatterns = [
    path("", include(router.urls)),
    path('avatar/', AvatarUpdateView.as_view(), name='avatar-update')
]

2) 테스트 결과 확인

profile
안녕하세요!

0개의 댓글