DRF ModelViewset 사용법

Posted on 2021-08-30 by GKSRUDTN99
Django Rest Framework DRF ModelViewSet

ModelViewSet을 왜 사용하는가?

DjangoFilterBackend, SearchFilter, OrderingFilter를 이용해 필터, 검색, 정렬이 쉽게 가능하다.

CRUD를 한번에 구현할 수 있다.


사용방법

1. django_filter를 pip로 설치하고, INSTALLED_APPSdjango_filters를 추가한다.

2. 필요한 모듈들을 임포트한다.

from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework import viewsets

3. ModelViewSet을 확장하는 새로운 클래스를 정의한다.

class ReviewViewSet(viewsets.ModelViewSet):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [permissions.AllowAny, ]

    filter_backends = [
        DjangoFilterBackend,
        OrderingFilter,
        SearchFilter,
    ]

    filterset_fields = ["user"]
    ordering_fields = ["upload_at"]
    search_fields = ["title"]

4. urls.py에 url을 등록한다.

  1. from django.urls import include
  2. router = DefaultRouter()router를 선언한다.
  3. router.register('', ReviewViewSet) 라우터를 등록한다.
  4. path('', include(router.urls)urlpatterns에 추가한다.
from django.urls import path, include
from .views import PostReviewView, ReviewListView, ReviewViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('', ReviewViewSet)

urlpatterns = [
    path('post/', PostReviewView.as_view(), name='post_review'),
    path('list/', ReviewListView.as_view(), name='review_list'),
    path('', include(router.urls)),
]

ModelViewSet의 추가적인 Custom 방법

ModelViewSet은 list, create, retrieve, update, partial_update, destroy의 6가지 action들을 가지고 있다.

예를 들어, 새로운 객체를 만들 때, user 필드를 로그인 된 유저로 자동으로 채우고 싶다면, 다음과 같이 custom할 수 있다.

class ReviewViewSet(viewsets.ModelViewSet):

    (... 생략 ...)

    def create(self, request):
        serializer = self.get_serializer(data=request.data)
        stylist = Stylist.objects.get(pk=request.data['stylist'])
        if serializer.is_valid() and stylist.user != self.request.user:
            serializer.save(user=self.request.user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST)

list필드를 override할 때, DjangoFilterBackend로 필터링을 하고 싶다면, self.filter_queryset(queryset)을 사용한다.

class MessageViewSet(viewsets.ModelViewSet):

    (... 생략 ...)

    def list(self, request, *args, **kwargs):
        messages = Message.objects.all().order_by('-created_at')
        messages = self.filter_queryset(messages)

        page = self.paginate_queryset(messages)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(page, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

action decorator를 통해 위의 6가지 기능 외 추가적인 기능도 구현이 가능하다.

from rest_framework.decorators import action
...
@action(detail=True, methods=['GET'], name='Bookmark')
    def bookmark(self, request, pk=None):
        if self.request.user.is_authenticated:
            stylist = Stylist.objects.get(pk=pk)
            if self.request.user.bookmarks.filter(pk=pk).count() == 1:
                self.request.user.bookmarks.remove(pk)
                stylist.like_count -= 1
            else:
                self.request.user.bookmarks.add(pk)
                stylist.like_count += 1
            self.request.user.save()
            stylist.save()
            return Response("OK", status=status.HTTP_200_OK)

        return Response("Not Authenticated", status=status.HTTP_401_UNAUTHORIZED)

기타

실습 환경

Django==3.2.4
django-cors-headers==3.7.0
djangorestframework==3.11.1
django-filter==2.4.0
Djagno와 DRF의 버전 문제로, TemplateNotExist문제나 ImportError가 발생하기도 한다.

DRF Viewset Reference

https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset