수학과의 좌충우돌 프로그래밍

[Django] DRF Pagination 본문

웹프로그래밍/DRF

[Django] DRF Pagination

사용자 ssung.k 2019. 12. 30. 00:29

실제 서비스의 경우에는 레코드의 개수가 많을 것이고 이 경우 하나의 API 요청으로 모든 레코드를 받는 것은 오랜 시간이 걸리게 됩니다. 따라서 이런 경우 페이지를 나눠서 요청을 해야합니다. 다행히도 DRF 에서는 이러한 pagination 기능을 제공하고 있습니다.

PageNumberPagination 과 LimitOffsetPagination

pagination 을 하는데는 크게 두 가지 방법이 있습니다.

두 방법 모두 url 의 get parameter 를 이용하여 이를 지원해줍니다.

각각에 대해 알아보도록 하겠습니다.

  • PageNumberPagination

    • page : 몇 번째 페이지인지 표시해줍니다. 페이지는 1부터 시작합니다.
    • page_size : 한 페이지에 몇 개의 레코드를 보여줄지 표시해줍니다.
  • LimitOffsetPagination

    • offset : 몇 번째 레코드부터 보여줄 지 설정해줍니다. 설정하지 않을 시 첫 번째 레코드 부터 보여줍니다.
    • limit : 몇 개의 레코드를 보여줄 지 설정합니다.
    • offset 번째 레코드부터 offset+limit-1 번째 레코드까지 보여줍니다.

 

전역 설정을 통해 Pagination

PageNumberPagination 이 더 사용 빈도가 많으니 이를 적용해보도록 하겠습니다.

settings 에서 기본적인 설정을 해줍니다.

어느 pagination을 할 지 결정하고, 그에 대한 parameter 값을 지정해줍니다.

# settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 3, 
}

 

그 결과 평상시와 좀 다른 응답이 왔습니다.

  • count : 전체 레코드의 수를 나타냅니다.
  • next, previous : 다음 페이지와 이전 페이지의 url 을 나타냅니다.
  • results : 이번 페이지에서의 레코드들을 나타냅니다.
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 5,
    "next": "http://localhost:8000/post/?page=2",
    "previous": null,
    "results": [
        {
            "id": 1,
            "title": "첫번째 제목",
            "content": "첫번째 내용",
            "is_public": false,
            "create_at": "2019-12-29T12:14:20.969581Z",
            "update_at": "2019-12-29T12:22:22.953888Z",
            "author": 1
        },
        {
            "id": 2,
            "title": "두번째 제목",
            "content": "두번째 내용",
            "is_public": false,
            "create_at": "2019-12-29T12:14:35.953629Z",
            "update_at": "2019-12-29T12:14:35.953671Z",
            "author": 1
        },
        {
            "id": 3,
            "title": "세번째 제목",
            "content": "세번째 내용",
            "is_public": false,
            "create_at": "2019-12-29T15:00:38.367163Z",
            "update_at": "2019-12-29T15:00:38.367214Z",
            "author": 1
        }
    ]
}

 

개별적으로 다른 Pagination

settings 를 통해서 설정을 해주게 되면 모든 view 에 대해서 같은 설정을 할 수 밖에 없습니다. 따라서 각각 개별적으로 설정하는 방법에 대해 알아보도록 하겠습니다. 이를 통해 전역적으로 설정되어있는 값을 덮어쓸 수도 있습니다.

우선 새로운 파일을 만들어 pagination 설정을 해줍니다.

PageNumberPagination을 상속받고 page_size 도 설정해주었습니다.

# pagination.py

from rest_framework.pagination import PageNumberPagination

class PostPageNumberPagination(PageNumberPagination):
    page_size = 2

 

view 에서는 이를 지정만 해주면 됩니다.

from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import SearchFilter
from .models import Post
from .serializers import PostSerializer
from .pagination import PostPageNumberPagination


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

 

개별적으로 사용해야하는 경우가 있더라도 우선 settings 에서 전체적인 값을 설정한 후 개별 설정을 하는 것을 추천드립니다.

'웹프로그래밍 > DRF' 카테고리의 다른 글

[Django] Authentication 과 Permissions  (0) 2020.01.08
[Django] Serializer 를 통한 유효성 검사 및 저장  (2) 2019.12.30
[Django] DRF Pagination  (6) 2019.12.30
[Django] filtering 과 Search  (0) 2019.12.29
[Django] format 과 Renderer  (0) 2019.12.29
[Django] ViewSet 과 Router  (8) 2019.12.28
6 Comments
  • 프로필사진 장고끝에악수 2021.09.01 17:44 안녕하세요! 우선 좋은 글 감사드립니다.

    질문이 하나 있는데요! 아래와 같이 PAGINATION 클래스와 PAGE_SIZE 정의하면, 자동적으로 페이지네이션 설정이 완료되는 것이 맞나요?

    # settings.py
    REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 3,
    }

    이렇게 했는데 GET 요청 때렸을 때 PAGINATION 적용이 안된거 같아서요.

    위 설정외에는 따로 추가한 코드는 없습니다.
  • 프로필사진 사용자 ssung.k 2021.09.01 23:20 신고 넵 되어야합니다. view 쪽 코드도 볼 수 있을까요?
  • 프로필사진 장고끝에악수 2021.09.02 10:33 우선 답변 정말 감사드려요!

    Global Pagination 사용하고 싶어서, 뷰 쪽 코드에서는 pagination 관련하여 따로 처리하진 않았습니다.

    또한, 저는 CBV로 구현하지 않고 FBV로 짜봤습니다.

    아래는 GET api/posts/ 에 대한 뷰 함수 구현입니다.

    ```
    @api_view(['GET', 'POST', 'PATCH', 'DELETE'])
    @permission_classes([IsAuthenticated])
    def posts_view(request, *args, **kwargs):
    post_id = kwargs.get('pk')
    if request.method == 'GET':
    # Get all posts that the request_user wrote
    post_qs = Post.objects.filter(author=request.user)
    post_serializer = PostGetSerailizer(post_qs, many=True)
    return Response(data=post_serializer.data, status=status.HTTP_200_OK)
    ```
  • 프로필사진 사용자 ssung.k 2021.09.03 00:32 신고 그럼 다음과 같이 Paginator를 써보는건 어떨까요
    https://ssungkang.tistory.com/entry/Django-11-Pagination-%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90
  • 프로필사진 장고끝에묘수 2021.09.06 10:55 감사합니다! 말씀주신 방법으로 해결했는데요, 그런데 위 링크의 방식대로하면 Global Pagination은 작동하지 않는 것 아닌가요?

    또한 위 방식대로 했을 때 (local pagination?) 다른점은, 페이지네이션에 대한 요청의 결과로 count, next, previous, results 등의 결괏값이 반환되지 않더라구요 ㅠㅠ

    이를 해결하는 방법이 있을까요?
  • 프로필사진 사용자 ssung.k 2021.09.14 00:21 신고 CBV와 FBV 차이라서 사용하는 방법이 다르실 겁니다!
댓글쓰기 폼