본문 바로가기

프로젝트/미술관 뒤 백엔드 지금은 전시상황

최종 팀 프로젝트[back] 상세보기 pagination

https://github.com/sdoram/b4_exhibitions_backend

 

GitHub - sdoram/b4_exhibitions_backend: B4조 지금은 전시상황 백엔드

B4조 지금은 전시상황 백엔드 . Contribute to sdoram/b4_exhibitions_backend development by creating an account on GitHub.

github.com

1. serializer를 복수 선언하기 

장점 : 필요한 경우의 수 만큼 선언하면 간단하게 해결 가능 

단점 : 필요한 종류에 따라서 serializer를 계속 선언하면 하나의 기능을 위한 수많은 serializer 생성 가능성 존재 

결론 : 최초 구현시 간단하게 쓸만한 느낌이지만, 최종적으로 사용하기엔 아쉬운 방법이다 

 

2. to_representation() 사용 

https://www.django-rest-framework.org/api-guide/serializers/#creating-new-base-classes

장점 : views에서 코드 가독성 증가 

단점 : 사용법 이해 부족, 코드를 이해하지 못하면 역으로 가독성 저하 

결론: 사용법을 익히는데 시간 소요가 있지만, 결과적으로 더 효율적인 코드 사용 가능 

1.to_representation 사용하기

 문제점

view에서 조건에 따라 다른 field를 불러와야 하는 상황 발생 

 시도해 본 것들

https://sdoram.tistory.com/183

 

TIL 23-06-09 serializer 고민하기

https://sdoram.tistory.com/182 최종 팀 프로젝트[front] navbar navbar 구현하는 방법들 1. 각각의 html에서 navbar를 다르게 구현하기 장점 : 각 페이지의 상황에 맞춰서 navbar구현 가능 단점 : html의 가독성 저하,

sdoram.tistory.com

    # views.py
    def get(self, request, exhibition_id):
        exhibition = get_object_or_404(Exhibition, id=exhibition_id)
        # query_params를 serializer로 전달
        serializer = ExhibitionDetailSerializer(
            exhibition, context={"select": request.query_params.get("select", None)}
        )

context에 query_parmas 정보 담아서 보내기 

 

ExhibitionDetailSerializer(serializers.ModelSerializer):
    """전시회 상세보기"""

    select = serializers.SerializerMethodField()
    # query_params에 따라서 필드 변경
    if select == "accompanies":
        accompanies = AccompanySerializer(many=True)
    else:
        reviews = ReviewSerializer(source="review_set", many=True

select가 serializer if문에서는 None으로 분기 처리가 안됨

 

ExhibitionDetailSerializer(serializers.ModelSerializer):
    """전시회 상세보기"""

    accompanies = AccompanySerializer(many=True)
    reviews = ReviewSerializer(source="review_set", many=True)

    # 읽기 전용 직렬화
    # serializer.data에서 select가 안된 다른 필드 값을 ''으로 변경
    def to_representation(self, instance):
        # serializer.data
        data = super().to_representation(instance)
        # query_params
        select = self.context["select"]
        # 데이터 값 빈값으로 교체
        if select == "accompanies":
            data["reviews"] = ""
        else:
            data["accompanies"] = ""
        return data

    class Meta:
        model = Exhibition

필요한 필드를 모두 가져온 뒤 읽기를 위한 to_representation안에서 select값 사용하기 

결과만 보면 어느정도 바라던 결과를 볼 수 있지만, 모든 데이터를 불러오고 필요없는 데이터를 공백으로 만들어서 보여주는 과정이 비효율적으로 느껴짐 

 

 해결 방법

    def to_representation(self, instance):
        # serializer.data
        data = super().to_representation(instance)
        # query_params
        select = self.context["select"]
        # select에 따라 filed 추가
        if select == "accompanies":
            accompany = instance.accompanies.all()
            serializer = AccompanySerializer(accompany, many=True)
            data["accompanies"] = serializer.data
        else:
            # related_name 설정 필요
            reviews = instance.review_set.all()
            serializer = ReviewSerializer(reviews, many=True)
            data["reviews"] = serializer.data
        return data

sererializer를 to_representation 내에서 선언하도록 변경 

이 과정에서 ForeignKey로 참조하는 과정에서 related_name이 선언된 필드와 아닌 필드 존재 

 알게 된 점

related_name이 무엇인지는 알고 있지만, 보통 전혀 안쓰거나, 모두 쓰거나 해서 크게 체감할 일이 없었는데 이번에 이 코드를 구현하며 정확하게 느낄 수 있었다. 

 

 

 

2. serializer내부 pagination 사용 

 문제점

하나의 게시글을 조회하고 그 게시글에 붙는 글의 페이지네이션이 필요함 

 시도해 본 것들

get_paginated_response() 알아보기

def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('count', self.page.paginator.count),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('results', data)
        ]))

serializer에서 사용하려 했지만 에러 발생 pagination.py에 선언된 method 찾아보기 

return Response라서 발생하는 에러로 추측 

 

class CustomPageNumberPagination(PageNumberPagination):
    PAGE_SIZE = 9

    def get_paginated_response(self, data):
        return OrderedDict(
            [
                ("count", self.page.paginator.count),
                ("next", self.get_next_link()),
                ("previous", self.get_previous_link()),
                ("results", data),
            ]
        )

CustomPageNumberPagination으로 오버라이딩 

 

 해결 방법

def to_representation(self, instance):
        # serializer.data
        data = super().to_representation(instance)
        pagination = CustomPageNumberPagination()
        # query_params
        select = self.context["select"]
        # select에 따라 field 추가
        if select == "accompanies":
            accompany = instance.accompanies.all()
            paginated_accompanies = pagination.paginate_queryset(
                accompany, self.context["request"]
            )
            serializer = AccompanySerializer(paginated_accompanies, many=True)
            data["accompanies"] = pagination.get_paginated_response(serializer.data)
        else:
            # related_name 설정 필요
            reviews = instance.review_set.all()
            paginated_reviews = pagination.paginate_queryset(
                reviews, self.context["request"]
            )
            serializer = ReviewSerializer(paginated_reviews, many=True)
            data["reviews"] = pagination.get_paginated_response(serializer.data)

데이터에 

OrderedDict(
            [
                ("count", self.page.paginator.count),
                ("next", self.get_next_link()),
                ("previous", self.get_previous_link()),
                ("results", data),
            ]
        )

OrderedDict에 담아서 전달 

 알게 된 점

레퍼런스가 없어도 시간을 들여서 사용해보면서 얻는 정보들이 유의미하다.

Collections 모듈에 포함되어 있는 OrderedDict는 순서까지 정해진 dict의 형태다.