[Django] 하나의 APIView에서 Serializer_class가 다를 때

haremeat·2022년 7월 14일
0

Django

목록 보기
9/15
post-thumbnail

목표

하나의 APIView에서 각각 가져와야 하는 serializer_class가 다를 때 어떤 식으로 해결해야하는지 araboja

진행과정

처음 코드는 생성 기능만을 수행하는 CreateAPIView였는데, 나는 지도와 그래프에서 불러오는 데이터 조회 기능을 여기에 붙이고 싶었다.

  • 기존 코드
class SensorDataView(CreateAPIView):
    permission_classes = [IsAuthenticated]
    queryset = SensorData.objects.all()
    serializer_class = SensorDataListSerializer
    filter_class = SensorDataFilter

generics.ListCreateAPIView를 사용해서 문제는 쉽게 해결되는 줄 알았으나...

문제가 생겼다. create 기능을 수행할 때와 list 기능을 수행할 때 가져와야 하는 serializer가 달랐던 것이다.

그래서 TypeError 메시지가 떴다.

위 오류를 해결하기 위해 같은 APIView 안에서 serializer가 다를 때 어떤 식으로 처리하는지 검색해보니 get_serializer_class 함수를 이용하면 쉽게 해결할 수 있다고 나왔다.

예를 들면 아래와 같이

class MyModelViewSet(viewsets.MyModelViewSet):

    queryset = MyModel.objects.all()
    serializer_class = MyModelListSerializer
    detail_serializer_class = MyModelDetailSerializer

    def get_serializer_class(self):
        if self.action == 'retrieve':
            if hasattr(self, 'detail_serializer_class'):
                return self.detail_serializer_class

        return super().get_serializer_class()

self.action을 이용해서 각 기능들을 예외처리 후 가져오는 방식이었다.

쉽게 해결했다! 하고 기뻐했는데 이번에는 아래의 오류 메시지가 떴다.

object has no attribute 'action’

삽질을 하다가 action 은 오로지 viewsets에서만 사용할 수 있다는 걸 뒤늦게 깨달았다. 나는 generics.ListCreateAPIView를 상속받고 action을 사용하고 있었기때문에 action값을 못 받아왔던 것이다.

ModelViewSet을 사용해도 기능에 문제는 없겠지만 필요한 기능은 오로지 생성과 목록뿐이었다.

최종적으로 내가 상속받은 건

mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet

이렇게 세 개였고 이렇게 self.action 오류를 해결하게 되었다.

최종코드는 다음과 같다.

class SensorDataView(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
    permission_classes = [IsAuthenticated]
    queryset = SensorData.objects.all()
    filter_class = SensorDataFilter

    serializer_class = SensorDataListSerializer
    serializer_classes = {
        'list': SensorDataSerializer,
        'create': SensorDataListSerializer
    }

    def get_serializer_class(self):
        if hasattr(self, 'serializer_classes'):
            return self.serializer_classes.get(self.action, self.serializer_class)

        return super().get_serializer_class()

처음에는 if문으로 처리했다가 선언형으로 처리하기 위해 serializer_classes라는 이름을 가진 dict를 만들어서 각 기능에 따라 serializer를 나눠뒀고

self.action이 list 기능을 수행할 때는 SensorDataSerializer를 사용하고,

self.action이 create기능을 수행할 때는 SensorDataListSerializer를 사용하도록 만들었다.

당장은 serializer가 달라지는 부분이 이 기능 하나뿐이기때문에 이렇게 처리하지만 아예 클래스를 빼서 상속받아 만드는 것이 가장 좋은 방법이라는 생각이 든다.

Ref

Viewsets - Django REST framework

Generic views

Object is not iterable in django rest

django-rest-framework/generics.py at 71e6c30034a1dd35a39ca74f86c371713e762c79 · encode/django-rest-framework

Django rest framework, use different serializers in the same ModelViewSet

How do I set different Serializer for list and detail view with Django Rest Framework?

profile
버그와 함께하는 삶

0개의 댓글