[DRF] PostList 리팩토링(feat. 'source='인자로 pk를 name으로 변경)

Jinhyung Rhee·2022년 8월 17일
1
post-thumbnail

Target : 템플릿 장고 PostList

  • 출력 결과

Now : DRF PostList (PostListAPIView)

  • 출력 결과
    • 1) pageCnt, curPage 없음 -> 생성 필요!
    • 2) category가 pk값으로 나오고 있음 -> name으로 변경 필요!
    • 3) image url 포맷이 다름!

1. Pagination 기능 추가

  • DRF에서 제공하는 pagination들 (링크)

    1. PageNumberPagination -> 가장 많이 사용!

      • 요청과 응답 형태
      • 적용 방법1) settings.py (global하게 적용)
      • 적용 방법2) view별로 pagination 클래스 적용
      • pagination 클래스 오버라이딩 가능
    2. LimitOffsetPagination

    3. CursorPagination

  • 코드

    • views.py

      # pagination overriding
      class PostPageNumberPagination(PageNumberPagination):
          page_size = 3
          # page_size_query_param = 'page_size'
          # max_page_size = 1000
      
      class PostListAPIView(ListAPIView):
        queryset = Post.objects.all()
        serializer_class = PostListSerializer
        pagination_class = PostPageNumberPagination
    • page_size = 3 : 한 페이지 당 항목 개수

    • pagination_class = PostPageNumberPagination : 오버라이딩한 페이지네이션 클래스를 해당 뷰의 페이지네이션 클래스로 적용

    • 결과

      • pagination이 적용된 결과로 count, next, previous, result 키가 등장함

      • key 이름 변경 필요!(PostList, pageCnt, curPage) -> PageNumberPagination 클래스 확인 후 수정(override)

        • paginate_queryset : 초기에 테이블로부터 데이터를 가져와서 지정된 개수(=3개씩)만큼 페이지를 구분

        • get_paginated_response : 페이지로 구분된 항목들을 응답에 사용

          • 여기서 출력 항목들을 정해주고 있었음!

          • 이 메서드를 가져와서 오버라이딩함

            # pagination overriding
            class PostPageNumberPagination(PageNumberPagination):
                page_size = 3 # 한 페이지 당 항목 개수
                # page_size_query_param = 'page_size'
                # max_page_size = 1000
                def get_paginated_response(self, data):
                    return Response(OrderedDict([
                        ('postList', data),
                        ('pageCnt', self.page.paginator.count),
                        ('curPage', self.page.paginator.count)
                    ]))
            
            class PostListAPIView(ListAPIView):
              queryset = Post.objects.all()
              serializer_class = PostListSerializer
              pagination_class = PostPageNumberPagination
            • self.page.paginator.count : count 값은 '총 항목의 개수'임!
          • 결과

            • key는 의도대로 잘 나오고 있음
            • value 수정이 필요함! -> Django Docs 확인
          • Django Docs

            • Pagination - Using Django : Pagination의 개념 설명

            • Paginator - API Reference✔ : Paginator의 속성이나 메서드를 설명하는 페이지 (링크)

              • Paginator.per_page : DRF의 page_size와 동일

              • Paginator.count : 총 항목의 개수

              • Paginator.num_pages✔ : 페이지의 개수

              • Page.number✔ : 현재 페이지의 번호

              • 이 항목들을 소스코드에 반영!

                  # pagination overriding
                  class PostPageNumberPagination(PageNumberPagination):
                      page_size = 3 # 한 페이지 당 항목 개수
                      # page_size_query_param = 'page_size'
                      # max_page_size = 1000
                      def get_paginated_response(self, data):
                          return Response(OrderedDict([
                              ('postList', data),
                              ('pageCnt', self.page.paginator.num_pages),
                              ('curPage', self.page.number),
                          ]))
                
                  class PostListAPIView(ListAPIView):
                    queryset = Post.objects.all()
                    serializer_class = PostListSerializer
                    pagination_class = PostPageNumberPagination
              • 반영된 결과

2. 카테고리 수정(pk->name)⭐

  • 카테고리가 pk로 나오는 이유

    • 5개의 필드만 정의되어 있음!
    • Django Shell로 확인
      >>> from api2.serializers import *
      >>> PostListSerializer()
      PostListSerializer():
          id = IntegerField(label='ID', read_only=True)
          title = CharField(label='TITLE', max_length=50)
          image = ImageField(allow_null=True, label='IMAGE', max_length=100, required=False)
          like = IntegerField(label='LIKE', required=False)
          category = PrimaryKeyRelatedField(allow_null=True, queryset=Category.objects.all(), required=False)
      • category 필드가 PrimaryKeyRelatedField로 정의되어 있기 때문에 pk로 표현되는 것!⭐⭐⭐
      • category 필드가 ForeignKey이기 때문에, DRF에서 자동으로 PrimaryKeyRelatedField로 잡은 것!
      • .을 통해서 특정 속성을 가져올 땐, 가져오려는 속성을 source= 인자로 넣어줌!⭐⭐⭐ → serializer 오버라이딩
        • ex) source='category.name'
  • serializer 오버라이딩⭐⭐⭐

    • 기존 serializers.py
      class PostListSerializer(serializers.ModelSerializer):
      class Meta:
          model = Post
          # fields = '__all__'
          fields = ['id', 'title', 'image', 'like', 'category']
      • 현재는 카테고리 필드에 대한 속성들이 자동으로 지정되어 있음!
      • 이를 오버라이딩하기 위해서는 클래스 변수로 필드를 재정의해줌!⭐⭐⭐
        class PostListSerializer(serializers.ModelSerializer):
             category = serializers.CharField(source='category.name')class Meta:
                 model = Post
                 # fields = '__all__'
                 fields = ['id', 'title', 'image', 'like', 'category']
    • 결과

3. ImageURL 포맷 변경

  • REST Framework의 ImageField() 확인 - rest_framework/fields.py
    • ImageField()
    • FileField() (=ImageField의 상위 클래스)
      • to_internal_value : serializer의 WRITE 모드 방향(역직렬화)
      • to_representation : serializer의 READ 모드 방향(직렬화)
        • build_absolute_uri() : FULL URL을 생성하는 메서드
        • context 변수를 가져와서 거기에 있는 request가 None이 아니라면 FULL URL을 만들고, request가 None이면 보통 URL을 만듦!⭐
          • 우리는 현재 보통 URL을 만들기 원함!
          • 방법1) to_representation 메서드를 오버라이딩 -> 출력 포맷을 결정하는 메서드이기 때문에 오버라이딩하는 경우 多
          • 방법2) context변수를 수정하는 메서드를 오버라이딩✔ -> 해당 메서드에서 request를 None으로 만들어버림!
  • ListAPIView -> GenericAPIView(상위클래스) 확인

    • get_serializer_context() : 여기서 context 변수로 'request', 'format', 'view'를 만들어주고 있음 -> 해당 함수의 request 부분을 None으로 바꾸면 보통 URL로 변경됨! (오버라이딩)
    • context : serializer에서 사용하는 중요한 변수
  • ListAPIView를 상속받는 PostListAPIView에 get_serializer_context 함수 오버라이딩!

    class PostListAPIView(ListAPIView):
        queryset = Post.objects.all()
        serializer_class = PostListSerializer
        pagination_class = PostPageNumberPagination
    
        def get_serializer_context(self):
              """
              Extra context provided to the serializer class.
              """
              return {
                  'request': None,'format': self.format_kwarg,
                  'view': self
              }
  • 결과

    • 이처럼 경로만 나오는 URL이 Django의 기본 URL 표현 방식임!

Reference

https://www.inflearn.com/course/%EC%9E%A5%EA%B3%A0-drf/dashboard

profile
기록하는 습관

0개의 댓글