pagination.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. from rest_framework import status
  2. from rest_framework.pagination import CursorPagination
  3. from rest_framework.response import Response
  4. from rest_framework.utils.urls import replace_query_param
  5. class LinkHeaderCursorPagination(CursorPagination):
  6. """
  7. Inform the user of pagination links via response headers, similar to what's
  8. described in https://developer.github.com/v3/guides/traversing-with-pagination/
  9. Inspired by the django-rest-framework-link-header-pagination package.
  10. """
  11. template = None
  12. @staticmethod
  13. def construct_headers(pagination_map):
  14. links = [f'<{url}>; rel="{label}"' for label, url in pagination_map.items() if url is not None]
  15. return {'Link': ', '.join(links)} if links else {}
  16. def get_paginated_response(self, data):
  17. pagination_required = self.has_next or self.has_previous
  18. if not pagination_required:
  19. return Response(data)
  20. url = self.request.build_absolute_uri()
  21. pagination_map = {'first': replace_query_param(url, self.cursor_query_param, '')}
  22. if self.cursor_query_param not in self.request.query_params:
  23. count = self.queryset.count()
  24. data = {
  25. 'detail': f'Pagination required. You can query up to {self.page_size} items at a time ({count} total). '
  26. 'Please use the `first` page link (see Link header).',
  27. }
  28. headers = self.construct_headers(pagination_map)
  29. return Response(data, headers=headers, status=status.HTTP_400_BAD_REQUEST)
  30. pagination_map.update(prev=self.get_previous_link(), next=self.get_next_link())
  31. headers = self.construct_headers(pagination_map)
  32. return Response(data, headers=headers)
  33. def paginate_queryset(self, queryset, request, view=None):
  34. self.request = request
  35. self.queryset = queryset
  36. return super().paginate_queryset(queryset, request, view)