Commit 1eb2dfcc by Tom Drummond

Added pagination mixin. Need to write tests

parent a53101be
...@@ -4,6 +4,7 @@ classes that can be added to a `View`. ...@@ -4,6 +4,7 @@ classes that can be added to a `View`.
""" """
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.core.paginator import Paginator
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ForeignKey
from django.http import HttpResponse from django.http import HttpResponse
...@@ -637,4 +638,92 @@ class ListModelMixin(object): ...@@ -637,4 +638,92 @@ class ListModelMixin(object):
queryset = queryset.order_by(*args) queryset = queryset.order_by(*args)
return queryset.filter(**kwargs) return queryset.filter(**kwargs)
########## Pagination Mixins ##########
class PaginatorMixin(object):
"""
Adds pagination support to GET requests
Obviously should only be used on lists :)
A default limit can be set by setting `limit` on the object. This will also
be used as the maximum if the client sets the `limit` GET param
"""
limit = 20
def get_limit(self):
""" Helper method to determine what the `limit` should be """
try:
limit = int(self.request.GET.get('limit', self.limit))
return min(limit, self.limit)
except ValueError:
return self.limit
def url_with_page_number(self, page_number):
""" Constructs a url used for getting the next/previous urls """
url = "%s?page=%d" % (self.request.path, page_number)
limit = self.get_limit()
if limit != self.limit:
url = "%s&limit=%d" % (url, limit)
return url
def next(self, page):
""" Returns a url to the next page of results (if any) """
if not page.has_next():
return None
return self.url_with_page_number(page.next_page_number())
def previous(self, page):
""" Returns a url to the previous page of results (if any) """
if not page.has_previous():
return None
return self.url_with_page_number(page.previous_page_number())
def serialize_page_info(self, page):
""" This is some useful information that is added to the response """
return {
'next': self.next(page),
'page': page.number,
'pages': page.paginator.num_pages,
'per_page': self.get_limit(),
'previous': self.previous(page),
'total': page.paginator.count,
}
def filter_response(self, obj):
"""
Given the response content, paginate and then serialize.
The response is modified to include to useful data relating to the number
of objects, number of pages, next/previous urls etc. etc.
The serialised objects are put into `results` on this new, modified
response
"""
# We don't want to paginate responses for anything other than GET requests
if self.method.upper() != 'GET':
return self._resource.filter_response(obj)
paginator = Paginator(obj, self.get_limit())
try:
page_num = int(self.request.GET.get('page', '1'))
except ValueError:
page_num = 1
if page_num not in paginator.page_range:
raise ErrorResponse(status.HTTP_404_NOT_FOUND, {'detail': 'That page contains no results'})
page = paginator.page(page_num)
serialized_object_list = self._resource.filter_response(page.object_list)
serialized_page_info = self.serialize_page_info(page)
serialized_page_info['results'] = serialized_object_list
return serialized_page_info
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment