diff --git a/docs/usage.md b/docs/usage.md index e7d91c62..ccab0dc8 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -34,6 +34,7 @@ record count and a `links` object with the next, previous, first, and last links Pages can be selected with the `page` GET parameter. Page size can be controlled per request via the `PAGINATE_BY_PARAM` query parameter (`page_size` by default). + ### Serializers It is recommended to import the base serializer classes from this package @@ -46,6 +47,37 @@ class MyModelSerializer(serializers.ModelSerializers): # ... ``` + +### Pagination + +This pacakge provides three paginators that correspond to the defaults +provided by django-rest-framework: PageNumberPagination, LimitOffsetPagination, +and CursorPagination. + +Each of these paginators will format the serialized data to conform to the +json-api spec for pagination as defined +[here](http://jsonapi.org/format/#fetching-pagination). + +To enable one of these paginators as the project default, you may add the +the following to your settings file: + +```python +REST_FRAMEWORK = { + ... + 'DEFAULT_PAGINATION_CLASS': + 'rest_framework_json_api.pagination.PageNumberPagination', +} +``` + +Alternatly you may define the paginator on the view itself as such: + +```python +from rest_framework_json_api import pagination +class FooViewSet(viewsets.ModelViewSet): + pagination_class = pagination.CursorPagination +``` + + ### Setting the resource_name You may manually set the `resource_name` property on views, serializers, or diff --git a/example/tests/integration/test_pagination.py b/example/tests/integration/test_pagination.py index 482ac460..615c4726 100644 --- a/example/tests/integration/test_pagination.py +++ b/example/tests/integration/test_pagination.py @@ -46,11 +46,11 @@ def test_pagination_with_single_entry(single_entry, client): } }], "links": { - "first": "http://testserver/entries?page=1", - "last": "http://testserver/entries?page=1", - "next": None, - "prev": None, - }, + "first": "http://testserver/entries?page=1", + "last": "http://testserver/entries?page=1", + "next": None, + "prev": None, + }, "meta": { "pagination": diff --git a/rest_framework_json_api/pagination.py b/rest_framework_json_api/pagination.py index 092a4450..e1ec44cb 100644 --- a/rest_framework_json_api/pagination.py +++ b/rest_framework_json_api/pagination.py @@ -2,17 +2,15 @@ Pagination fields """ from collections import OrderedDict -from rest_framework import serializers +from rest_framework import pagination from rest_framework.views import Response -from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination from rest_framework.utils.urls import remove_query_param, replace_query_param -class PageNumberPagination(PageNumberPagination): +class PageNumberPagination(pagination.PageNumberPagination): """ A json-api compatible pagination format """ - page_size_query_param = 'page_size' max_page_size = 100 @@ -49,7 +47,7 @@ def get_paginated_response(self, data): }) -class LimitOffsetPagination(LimitOffsetPagination): +class LimitOffsetPagination(pagination.LimitOffsetPagination): """ A limit/offset based style. For example: http://api.example.org/accounts/?page[limit]=100 @@ -75,10 +73,10 @@ def get_last_link(self): def get_first_link(self): if self.count == 0: return None - + url = self.request.build_absolute_uri() return remove_query_param(url, self.offset_query_param) - + def get_paginated_response(self, data): return Response({ 'results': data, @@ -95,4 +93,19 @@ def get_paginated_response(self, data): ('next', self.get_next_link()), ('prev', self.get_previous_link()) ]) - }) \ No newline at end of file + }) + + +class CursorPagination(pagination.CursorPagination): + """ + Cursor paginator that adheres to the json-api spec. + (http://jsonapi.org/examples/#pagination) + """ + def get_paginated_response(self, data): + return Response({ + 'results': data, + 'links': OrderedDict([ + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ]) + }) diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py index 098c6700..21d849f4 100644 --- a/rest_framework_json_api/renderers.py +++ b/rest_framework_json_api/renderers.py @@ -5,7 +5,7 @@ from collections import OrderedDict import inflection -from django.db.models import Manager, QuerySet +from django.db.models import Manager from django.utils import six, encoding from rest_framework import relations from rest_framework import renderers @@ -396,7 +396,7 @@ def build_json_resource_obj(cls, fields, resource, resource_instance, resource_n resource_data.append(('links', {'self': resource[api_settings.URL_FIELD_NAME]})) return OrderedDict(resource_data) - def render_relationship_view(self, data, accepted_media_type=None, renderer_context=None): + def render_relationship_view(self, data, accepted_media_type=None, renderer_context={}): # Special case for RelationshipView view = renderer_context.get("view", None) render_data = OrderedDict([ @@ -409,12 +409,12 @@ def render_relationship_view(self, data, accepted_media_type=None, renderer_cont render_data, accepted_media_type, renderer_context ) - def render_errors(self, data, accepted_media_type=None, renderer_context=None): + def render_errors(self, data, accepted_media_type=None, renderer_context={}): return super(JSONRenderer, self).render( utils.format_errors(data), accepted_media_type, renderer_context ) - def render(self, data, accepted_media_type=None, renderer_context=None): + def render(self, data, accepted_media_type=None, renderer_context={}): view = renderer_context.get("view", None) request = renderer_context.get("request", None)