Skip to content

Issue 430: pagination enhancement #434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v2.5.0 - [unreleased]
* Add new pagination classes based on JSON:API recommendations.

v2.4.0 - Released January 25, 2018

* Add support for Django REST Framework 3.7.x.
Expand Down
55 changes: 48 additions & 7 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ REST_FRAMEWORK = {
'PAGE_SIZE': 10,
'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler',
'DEFAULT_PAGINATION_CLASS':
'rest_framework_json_api.pagination.PageNumberPagination',
'rest_framework_json_api.pagination.JsonApiPageNumberPagination',
'DEFAULT_PARSER_CLASSES': (
'rest_framework_json_api.parsers.JSONParser',
'rest_framework.parsers.FormParser',
Expand All @@ -34,14 +34,55 @@ REST_FRAMEWORK = {
}
```

If `PAGE_SIZE` is set the renderer will return a `meta` object with
### Pagination

DJA pagination is based on [DRF pagination](http://www.django-rest-framework.org/api-guide/pagination/).

When pagination is enabled, the renderer will return a `meta` object with
record count and a `links` object with the next, previous, first, and last links.
Pages can be selected with the `page` GET parameter. The query parameter used to
retrieve the page can be customized by subclassing `PageNumberPagination` and
overriding the `page_query_param`. Page size can be controlled per request via
the `PAGINATE_BY_PARAM` query parameter (`page_size` by default).

#### Performance Testing
#### Configuring the Pagination Style

Pagination style can be set on a particular viewset with the `pagination_class` attribute or by default for all viewsets
by setting `REST_FRAMEWORK['DEFAULT_PAGINATION_CLASS']` and by setting `REST_FRAMEWORK['PAGE_SIZE']`.

You can configure fixed values for the page size or limit -- or allow the client to choose the size or limit
via query parameters.

Two pagination classes are available:
- `JsonApiPageNumberPagination` breaks a response up into pages that start at a given page number with a given size
(number of items per page). It can be configured with the following attributes:
- `page_query_param` (default `page[number]`)
- `page_size_query_param` (default `page[size]`) Set this to `None` if you don't want to allow the client
to specify the size.
- `max_page_size` (default `100`) enforces an upper bound on the `page_size_query_param`.
Set it to `None` if you don't want to enforce an upper bound.
- `JsonApiLimitOffsetPagination` breaks a response up into pages that start from an item's offset in the viewset for
a given number of items (the limit).
It can be configured with the following attributes:
- `offset_query_param` (default `page[offset]`).
- `limit_query_param` (default `page[limit]`).
- `max_limit` (default `100`) enforces an upper bound on the limit.
Set it to `None` if you don't want to enforce an upper bound.


These examples show how to configure the parameters to use non-standard names and different limits:

```python
from rest_framework_json_api.pagination import JsonApiPageNumberPagination, JsonApiLimitOffsetPagination

class MyPagePagination(JsonApiPageNumberPagination):
page_query_param = 'page_number'
page_size_query_param = 'page_size'
max_page_size = 1000

class MyLimitPagination(JsonApiLimitOffsetPagination):
offset_query_param = 'offset'
limit_query_param = 'limit'
max_limit = None
```

### Performance Testing

If you are trying to see if your viewsets are configured properly to optimize performance,
it is preferable to use `example.utils.BrowsableAPIRendererWithoutForms` instead of the default `BrowsableAPIRenderer`
Expand Down
39 changes: 35 additions & 4 deletions rest_framework_json_api/pagination.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
"""
Pagination fields
"""
import warnings
from collections import OrderedDict

from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
from rest_framework.utils.urls import remove_query_param, replace_query_param
from rest_framework.views import Response


class PageNumberPagination(PageNumberPagination):
class JsonApiPageNumberPagination(PageNumberPagination):
"""
A json-api compatible pagination format
"""

page_size_query_param = 'page_size'
page_query_param = 'page[number]'
page_size_query_param = 'page[size]'
max_page_size = 100

def build_link(self, index):
Expand Down Expand Up @@ -49,14 +50,15 @@ def get_paginated_response(self, data):
})


class LimitOffsetPagination(LimitOffsetPagination):
class JsonApiLimitOffsetPagination(LimitOffsetPagination):
"""
A limit/offset based style. For example:
http://api.example.org/accounts/?page[limit]=100
http://api.example.org/accounts/?page[offset]=400&page[limit]=100
"""
limit_query_param = 'page[limit]'
offset_query_param = 'page[offset]'
max_limit = 100

def get_last_link(self):
if self.count == 0:
Expand Down Expand Up @@ -96,3 +98,32 @@ def get_paginated_response(self, data):
('prev', self.get_previous_link())
])
})


class PageNumberPagination(JsonApiPageNumberPagination):
"""
Deprecated paginator that uses different query parameters
"""
page_query_param = 'page'
page_size_query_param = 'page_size'

def __init__(self):
warnings.warn(
'PageNumberPagination is deprecated. Use JsonApiPageNumberPagination '
'or create custom pagination. See '
'http://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination',
DeprecationWarning)


class LimitOffsetPagination(JsonApiLimitOffsetPagination):
"""
Deprecated paginator that uses a different max_limit
"""
max_limit = None

def __init__(self):
warnings.warn(
'LimitOffsetPagination is deprecated. Use JsonApiLimitOffsetPagination '
'or create custom pagination. See '
'http://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination',
DeprecationWarning)