cursorpaginator.py 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. from django.core.paginator import EmptyPage, InvalidPage
  2. class CursorPaginator:
  3. def __init__(self, queryset, order_by, per_page):
  4. self.queryset = queryset
  5. self.order_by = order_by
  6. self.per_page = int(per_page)
  7. def get(self, start=0):
  8. if start < 0:
  9. raise InvalidPage()
  10. object_list = list(self._get_slice(start))
  11. if start and not object_list:
  12. raise EmptyPage()
  13. next_cursor = None
  14. if len(object_list) > self.per_page:
  15. next_slice_first_item = object_list.pop(-1)
  16. attr_name = self.order_by.lstrip("-")
  17. next_cursor = getattr(next_slice_first_item, attr_name)
  18. return Page(start, object_list, next_cursor)
  19. def _get_slice(self, start):
  20. page_len = self.per_page + 1
  21. if start:
  22. if self.order_by.startswith("-"):
  23. filter_name = "%s__lte" % self.order_by[1:]
  24. else:
  25. filter_name = "%s__gte" % self.order_by
  26. return self.queryset.filter(**{filter_name: start})[:page_len]
  27. return self.queryset[:page_len]
  28. class Page:
  29. def __init__(self, start, object_list, next_):
  30. self.start = start or 0
  31. self.object_list = object_list
  32. self.next = next_
  33. def __len__(self):
  34. return len(self.object_list)
  35. def has_next(self):
  36. return bool(self.next)