aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Drummond2011-07-19 17:06:56 +0100
committerTom Drummond2011-07-19 17:06:56 +0100
commit1eb2dfcc3a84deb21aeae5a427cc8b11f578c00c (patch)
tree1a9babcad67792b0cd6d72ebb8fc91a5f3dae708
parenta53101bea9f6fbf7d7528a7c4aba3d83e3dbc241 (diff)
downloaddjango-rest-framework-1eb2dfcc3a84deb21aeae5a427cc8b11f578c00c.tar.bz2
Added pagination mixin. Need to write tests
-rw-r--r--djangorestframework/mixins.py89
1 files changed, 89 insertions, 0 deletions
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
index bb26ad96..56f17393 100644
--- a/djangorestframework/mixins.py
+++ b/djangorestframework/mixins.py
@@ -4,6 +4,7 @@ classes that can be added to a `View`.
"""
from django.contrib.auth.models import AnonymousUser
+from django.core.paginator import Paginator
from django.db.models.query import QuerySet
from django.db.models.fields.related import ForeignKey
from django.http import HttpResponse
@@ -637,4 +638,92 @@ class ListModelMixin(object):
queryset = queryset.order_by(*args)
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