하나의 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가 달라지는 부분이 이 기능 하나뿐이기때문에 이렇게 처리하지만 아예 클래스를 빼서 상속받아 만드는 것이 가장 좋은 방법이라는 생각이 든다.
Viewsets - Django REST framework
Object is not iterable in django rest
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?