HJ Works

[Django REST Framework] ViewSet 이용과 Customize 본문

Dev/Python & Django

[Django REST Framework] ViewSet 이용과 Customize

HJ Works 2021. 7. 20. 23:50

Django를 이용한 웹 개발시 Function Based View 또는 Class Based View를 복합적으로 사용 가능하다. Django 서비스 개발이 성숙하고, 구조적인 설계를 지향하다 보면 CBV를 쓰게 된다.

Django REST Framework 를 사용하면, 여기서 한단계 더 진화한 ViewSet 을 쓰게 된다. ViewSet을 이용하면 한개의 CBV가 여러개의 인터페이스를 자동으로 만들어준다.

매우 간단한 예로 다음과 같은 코드를 만들 수 있다.

기본적인 사용법

views.py

class SimpleViewSet(viewsets.ViewSet):
    def list(self, request):
        queryset = myQuerySet()
        serializer = SimpleSerializer(queryset, many=True)
        return Response(serializer.data)
    def retrive(self, request, pk=None):
        queryset = myQuerySet()
        obj = get_object_or_404(Object, pk=pk)
        serializer = SimpleSerializer(queryset, many=True)
        return Response(serializer.data)
    # 이 외에도 create(self), update(self), destroy(self) 등을 override 가능하다.

urls.py

from rest_framework.routers import DefaultRouter
from .views import SimpleViewSet

router = DefaultRouter()
router.register('simple', SimpleViewSet)

urlpatterns = router.get_urls()

위와 같이 url을 완성하면, 별도의 URL conf 설정을 하지 않아도 다음과 같은 표준 URL interface가 작성된다.

즉, DRF의 ViewSet과 Router를 조합하여 CRUD에 필요한 대부분의 URL이 자동으로 생성되는 것이다.

simple/
simple/?P<pk>

추가 URL 설정하기

또한, abstract method 외에도 직접 method 추가를 할 수 있다.

views.py

class SimpleViewSet(viewsets.ViewSet):

    ...

    @action
    def customize(self):
       queryset = myCustomQuery()
       return Response(Serialize(queryset).data)
simple/
simple/?P<pk>
simple/customize

Model 있는 ViewSet 작성하기

사실 ViewSet이 진정한 능력을 발휘할 때는 Model Based View를 만들 때이다.

GenericViewSet과 Mixin을 적절히 조합해서, 대부분의 method를 작성하지 않아도 충분한 작업을 할 수 있다.

models.py

class SimpleModel(models.Model):
    foo = models.CharField()
    bar = models.Integerfield()

views.py

from rest_framework viewsets, mixins

class SimpleModelViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):

    queryset = mySimpleQuerySet()
    serializer_class = mySimpleSerializer

urls.py

from rest_framework.routers import DefaultRouter
from .views import SimpleModelViewSet

router = DefaultRouter()
router.register('simple', SimpleModelViewSet)

urlpatterns = router.get_urls()

위와 같이, 기존의 DRF View를 쓸 때와 마찬가지로 queryset, serializer_class를 지정해 두면, 이 내용이 URL에 포함된다.

만약 viewsets.ModelViewSet을 이용한다면, 해당 Model 의 모든 작업 (CRUD) 이 가능하다.

하지만, 해당 View 목적에 따라 GenericViewSet 과 일부 Mixin만을 가져올 수 있다.

위의 사례는 viewsets.ReadOnlyModelViewSet과 동일한 역할을 하며,

이 경우 URL이 아래와 같이 되나 GET/HTTP만 이용하게 된다.

simple/
simple/(?P<pk>)
...

따라서, 위의 SimpleModelViewSet에 CreateModelMixin 을 추가하면, post 데이터를 기반으로 create 작업을 할 수 있게 되는 것이다.

조금 더 튜닝하기

ModelViewSet에서 PK를 조작하기

Model을 이용하는 GenericViewSet 과 ModelMixin의 경우, 연결된 DB의 PK를 자동으로 가져오게 된다.

그래서 위와 같이 pk가 붙게 된다. 예를 들면 다음과 같이 된다.

https://my-service.com/api/simple  # 이 URL은 simple DB 전체를 가져온다.
https://my-service.com/api/simple/1  # 이 URL은 simple DB 중 PK 1에 해당하는 값을 가져온다.

별도 설정을 하지 않으면 pk가 제일 뒤에 URL suffix로 붙는다. pk가 아닌 value를 쓰고 싶거나, 이 위치를 바꿀 수 있다.

urls.py

from rest_framework.routers import DefaultRouter
from .views import SimpleModelViewSet

router = DefaultRouter()
router.register(r'simple/(?P<non_pk_value>[regex]', SimpleModelViewSet)

urlpatterns = router.get_urls()

위 router는 non_pk_value를 이용해서 filter된 결과를 가져오려고 한다.

다만, 이 경우 RetrieveMixin이 활성화된다. 결국 non_pk_value 가 해당 테이블에서 유니크하지 않다면 get_object()multiple object returns 예외를 일으킬 것이라서 유의하여야 한다.

views.py

from rest_framework viewsets, mixins

class SimpleModelViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):
    serializer_class = mySimpleSerializer

    def get_queryset(self):
        normal_value = self.kwargs.get('non_pk_value')
        qs = Simple.objects.filter(value=normal_value)
        return qs

이런 형태로 non_pk_value를 queryset에서 설정하게 된다. 다만, 여기서 qs는 1개만 리턴한다고 전제되어야 한다.(위의 get_object 가 에러를 일으키게 된다.)