인프런 커뮤니티 질문&답변

조느단님의 프로필 이미지
조느단

작성한 질문수

Django REST Framework 핵심사항

PostSerializerDetail serializer를 이용해 직렬화하고, 해당 ViewSet을 router를 통해 url 매핑을 한 후 api2/post/<int:pk>(get) 엔드포인트에 request를 하면 에러가 발생합니다.

작성

·

284

0

* VueDjAgencyDrf-untilCh7-2의 api2/views.py에서 (프로그래머가 직접 직렬화하는 것이 아니라 VueDjAgencyDrf-untilCh6의) PostSerializerDetail serializer를 이용해 직렬화하고, 해당 ViewSet을 router를 통해 url 매핑을 하면 아래와 같은 에러가 발생합니다...
* 제가 추가한 코드는 다음과 같습니다.

 

# api2/urls.py
router = routers.DefaultRouter()
router.register(r'post', views.PostViewSet)

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

# api2/views.py
def get_prev_next(instance):
    try:
        prev = instance.get_previous_by_update_dt()
    except instance.DoesNotExist:
        prev = None

    try:
        next_ = instance.get_next_by_update_dt()
    except instance.DoesNotExist:
        next_ = None

    return prev, next_

class PostViewSet(ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer
    pagination_class = PostPageNumberPagination

    def get_serializer_context(self):
        return {
            'request': None,
            'format': self.format_kwarg,
            'view': self
        }

    def get_queryset(self):
        return Post.objects.all().select_related('category').prefetch_related('tags', 'comment_set')

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        prevInstance, nextInstance = get_prev_next(instance)
        commentList = instance.comment_set.all()
        data = {
            'post': instance,
            'prevPost': prevInstance,
            'nextPost': nextInstance,
            'commentList': commentList,
        }
        serializer = PostSerializerDetail(instance=data)
        return Response(serializer.data)
 
* 그런데 이 이슈는 아래와 같이 router를 쓰지 않으면 발생하지 않고 정상 동작합니다.
    path('post/', views.PostViewSet.as_view(actions={
        'get': 'list',
    }), name='post-list'),
    path('post/<int:pk>/', views.PostViewSet.as_view(actions={
        'get': 'retrieve',
    }), name='post-detail'),
    path('post/<int:pk>/like/', views.PostViewSet.as_view(actions={
        'get': 'like',
    }), name='post-like'),

* 어떻게 하면 PostSerializerDetail를 통해 직렬화하고 해당 ViewSet이 router를 이용하도록 하여도 에러가 나지 않을 수 있을까요?

답변 2

1

김석훈님의 프로필 이미지
김석훈
지식공유자

/api2/post/2/?format=json 으로 해 볼래요. 그 차이를 알 수 있을 것입니다.

이 처리과정이 기본 로직이고,

질문 내용은, /api2/post/2/ 에 대한 응답으로 Browsable API 화면을 만드는 과정에서,

PUT/PATCH 용 폼을 보여주기 위해서, get_serializer_class() 를 호출하는 것입니다.

이 폼을 처리하는 로직은 알면 좋겠지만 그리 중요해 보이지는 않습니다만...

즉 일반적인 시리얼라이저 로직 플로우는 아닌 것 입니다.

참고하세요.

1

김석훈님의 프로필 이미지
김석훈
지식공유자

안녕하세요. 독자님.

에러가 나는 원인은, list/retrieve 처리에 시리얼라이저가 다르고,

상단에 있는 serializer_class=PostListSerializer 가 디폴트 시리얼라이저로 작동하기 때문입니다.

상단의 serializer_class 라인을 삭제하고, 아래 내용을 추가해 보세요.

    def get_serializer_class(self):
        if self.action == 'list':
            return PostListSerializer
        if self.action == 'retrieve':
            return PostSerializerDetail
        return PostSerializerDetail

 

김석훈님의 프로필 이미지
김석훈
지식공유자

    def retrieve(self, request, *args, **kwargs):
        . . .
        # serializer = PostSerializerDetail(instance=data)
        serializer = self.get_serializer(instance=data)
        return Response(serializer.data)

이 부분도 수정이 필요하네요.

 

조느단님의 프로필 이미지
조느단
질문자

감사합니다! 그렇게 하니까 해결이 되네요.

그런데 주신 코드를 아래와 같이 수정하면 get_serializer_class가 총 4회에 걸쳐 실행되고, 처음 액션은 retrieve, 그 이후에는 각각 update, partial_update, update로 찍히는데요 혹시

1)어디에서 get_serializer_class가 호출되는지,

2)왜 액션이 update와 partail_update인지 설명해주실 수 있으신지요^^??

제가 작성한 코드

class PostViewSet(ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostListSerializer
    pagination_class = PostPageNumberPagination

    def get_serializer_class(self):
        if self.action == 'list':
            print(self.action, 111)
            return PostListSerializer
        if self.action == 'retrieve':
            print(self.action, 222)
            return PostSerializerDetail
        print(self.action, 333)
        return PostSerializerDetail

    def update(self, request, *args, **kwargs):
        print("update working")
        return super().update(request, *args, **kwargs)

    def partial_update(self, request, *args, **kwargs):
        print("partial working")
        return super().partial_update(request, *args, **kwargs)

    def get_queryset(self):
        return Post.objects.all().select_related('category').prefetch_related('tags', 'comment_set')

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        prevInstance, nextInstance = get_prev_next(instance)
        commentList = instance.comment_set.all()
        data = {
            'post': instance,
            'prevPost': prevInstance,
            'nextPost': nextInstance,
            'commentList': commentList,
        }
        serializer = self.get_serializer(instance=data)
        return Response(serializer.data)

    def get_serializer_context(self):
        return {
            'request': None,
            'format': self.format_kwarg,
            'view': self
        }

    def like(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.like += 1
        instance.save()
        return Response(instance.like)

코드가 실행되었을 때 찍힌 로그

retrieve working

retrieve 222

update 333

partial_update 333

update 333

 

 

조느단님의 프로필 이미지
조느단

작성한 질문수

질문하기