Skip to content

Commit 697adf1

Browse files
committed
Merge pull request #205 from sarendsen/develop
Add LimitOffsetPagination
2 parents eaaad66 + 36d0fc3 commit 697adf1

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

example/tests/unit/test_pagination.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from collections import OrderedDict
2+
3+
from rest_framework.request import Request
4+
from rest_framework.test import APIRequestFactory
5+
from rest_framework.utils.urls import replace_query_param
6+
7+
from rest_framework_json_api.pagination import LimitOffsetPagination
8+
9+
10+
factory = APIRequestFactory()
11+
12+
13+
class TestLimitOffset:
14+
"""
15+
Unit tests for `pagination.LimitOffsetPagination`.
16+
"""
17+
18+
def setup(self):
19+
class ExamplePagination(LimitOffsetPagination):
20+
default_limit = 10
21+
max_limit = 15
22+
23+
self.pagination = ExamplePagination()
24+
self.queryset = range(1, 101)
25+
self.base_url = 'http://testserver/'
26+
27+
def paginate_queryset(self, request):
28+
return list(self.pagination.paginate_queryset(self.queryset, request))
29+
30+
def get_paginated_content(self, queryset):
31+
response = self.pagination.get_paginated_response(queryset)
32+
return response.data
33+
34+
def get_test_request(self, arguments):
35+
return Request(factory.get('/', arguments))
36+
37+
def test_valid_offset_limit(self):
38+
"""
39+
Basic test, assumes offset and limit are given.
40+
"""
41+
offset = 10
42+
limit = 5
43+
count = len(self.queryset)
44+
last_offset = count - limit
45+
next_offset = 15
46+
prev_offset = 5
47+
48+
request = self.get_test_request({
49+
self.pagination.limit_query_param: limit,
50+
self.pagination.offset_query_param: offset
51+
})
52+
base_url = replace_query_param(self.base_url, self.pagination.limit_query_param, limit)
53+
last_url = replace_query_param(base_url, self.pagination.offset_query_param, last_offset)
54+
first_url = base_url
55+
next_url = replace_query_param(base_url, self.pagination.offset_query_param, next_offset)
56+
prev_url = replace_query_param(base_url, self.pagination.offset_query_param, prev_offset)
57+
queryset = self.paginate_queryset(request)
58+
content = self.get_paginated_content(queryset)
59+
next_offset = offset + limit
60+
61+
expected_content = {
62+
'results': list(range(offset + 1, next_offset + 1)),
63+
'links': OrderedDict([
64+
('first', first_url),
65+
('last', last_url),
66+
('next', next_url),
67+
('prev', prev_url),
68+
]),
69+
'meta': {
70+
'pagination': OrderedDict([
71+
('count', count),
72+
('limit', limit),
73+
('offset', offset),
74+
])
75+
}
76+
}
77+
78+
assert queryset == list(range(offset + 1, next_offset + 1))
79+
assert content == expected_content

rest_framework_json_api/pagination.py

+51-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from collections import OrderedDict
55
from rest_framework import serializers
66
from rest_framework.views import Response
7-
from rest_framework.pagination import PageNumberPagination
8-
from rest_framework.templatetags.rest_framework import replace_query_param
7+
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
8+
from rest_framework.utils.urls import remove_query_param, replace_query_param
99

1010

1111
class PageNumberPagination(PageNumberPagination):
@@ -47,3 +47,52 @@ def get_paginated_response(self, data):
4747
('prev', self.build_link(previous))
4848
])
4949
})
50+
51+
52+
class LimitOffsetPagination(LimitOffsetPagination):
53+
"""
54+
A limit/offset based style. For example:
55+
http://api.example.org/accounts/?page[limit]=100
56+
http://api.example.org/accounts/?page[offset]=400&page[limit]=100
57+
"""
58+
limit_query_param = 'page[limit]'
59+
offset_query_param = 'page[offset]'
60+
61+
def get_last_link(self):
62+
if self.count == 0:
63+
return None
64+
65+
url = self.request.build_absolute_uri()
66+
url = replace_query_param(url, self.limit_query_param, self.limit)
67+
68+
offset = self.count - self.limit
69+
70+
if offset <= 0:
71+
return remove_query_param(url, self.offset_query_param)
72+
73+
return replace_query_param(url, self.offset_query_param, offset)
74+
75+
def get_first_link(self):
76+
if self.count == 0:
77+
return None
78+
79+
url = self.request.build_absolute_uri()
80+
return remove_query_param(url, self.offset_query_param)
81+
82+
def get_paginated_response(self, data):
83+
return Response({
84+
'results': data,
85+
'meta': {
86+
'pagination': OrderedDict([
87+
('count', self.count),
88+
('limit', self.limit),
89+
('offset', self.offset),
90+
])
91+
},
92+
'links': OrderedDict([
93+
('first', self.get_first_link()),
94+
('last', self.get_last_link()),
95+
('next', self.get_next_link()),
96+
('prev', self.get_previous_link())
97+
])
98+
})

0 commit comments

Comments
 (0)