Last active
August 29, 2015 14:01
-
-
Save flisky/4986e51b403ddb2e22c5 to your computer and use it in GitHub Desktop.
countless django paginator for django-rest-framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import collections | |
from django.utils import six | |
from django.core.paginator import PageNotAnInteger, EmptyPage | |
class Paginator(object): | |
def __init__(self, object_list, per_page, orphans=0, | |
allow_empty_first_page=True): | |
self.object_list = object_list | |
self.per_page = int(per_page) | |
self.orphans = int(orphans) | |
self.allow_empty_first_page = allow_empty_first_page | |
self._num_pages = self._count = None | |
def validate_number(self, number): | |
""" | |
Validates the given 1-based page number. | |
""" | |
try: | |
number = int(number) | |
except (TypeError, ValueError): | |
raise PageNotAnInteger('That page number is not an integer') | |
if number < 1: | |
raise EmptyPage('That page number is less than 1') | |
return number | |
def page(self, number): | |
""" | |
Returns a Page object for the given 1-based page number. | |
""" | |
number = self.validate_number(number) | |
bottom = (number - 1) * self.per_page | |
top = bottom + self.per_page | |
return Page(self.object_list[bottom:top + 1], number, self) | |
class Page(collections.Sequence): | |
def __init__(self, object_list, number, paginator): | |
object_length = len(object_list) | |
if object_length > paginator.per_page: | |
self.object_list = object_list[:object_length - 1] | |
self._has_next = True | |
else: | |
self.object_list = object_list | |
self._has_next = False | |
# force evaluate queryset to get rid of all function | |
# since DRF checks it and then call with queryset.all | |
# which will hit db due to different queryset | |
self.object_list = list(object_list) | |
self.number = number | |
self.paginator = paginator | |
def __repr__(self): | |
return '<Page %s of N/A>' % (self.number,) | |
def __len__(self): | |
return len(self.object_list) | |
def __getitem__(self, index): | |
if not isinstance(index, (slice,) + six.integer_types): | |
raise TypeError | |
# The object_list is converted to a list so that if it was a QuerySet | |
# it won't be a database hit per __getitem__. | |
if not isinstance(self.object_list, list): | |
self.object_list = list(self.object_list) | |
return self.object_list[index] | |
def has_next(self): | |
return self._has_next | |
def has_previous(self): | |
return self.number > 1 | |
def has_other_pages(self): | |
return self.has_previous() or self.has_next() | |
def next_page_number(self): | |
if not self._has_next: | |
if self.number == 1 and self.allow_empty_first_page: | |
pass | |
else: | |
raise EmptyPage('That page contains no results') | |
return self.number + 1 | |
def previous_page_number(self): | |
if self.number < 1: | |
raise EmptyPage('That page number is less than 1') | |
return self.number - 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment