Skip to content

Commit e047e60

Browse files
committed
Improve type checking in pagination.py
1 parent e8b2300 commit e047e60

File tree

3 files changed

+85
-35
lines changed

3 files changed

+85
-35
lines changed

styleguide_example/api/pagination.py

+80-30
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
11
from collections import OrderedDict
2+
from typing import Optional, Sequence, Tuple, Type, Union
23
from urllib.parse import parse_qs, urlparse
34

4-
from rest_framework.pagination import LimitOffsetPagination as _LimitOffsetPagination
5+
from django.db.models import QuerySet
6+
from rest_framework import serializers
7+
from rest_framework.pagination import BasePagination
58
from rest_framework.pagination import CursorPagination as _CursorPagination
9+
from rest_framework.pagination import LimitOffsetPagination as _LimitOffsetPagination
10+
from rest_framework.request import Request
611
from rest_framework.response import Response
12+
from rest_framework.views import APIView
713

814

9-
def get_paginated_response(*, pagination_class, serializer_class, queryset, request, view):
10-
paginator = pagination_class()
15+
class _TurnOffPaginationSerializer(serializers.Serializer):
16+
paginate = serializers.BooleanField(default=True)
1117

12-
page = paginator.paginate_queryset(queryset, request, view=view)
1318

14-
if page is not None:
15-
serializer = serializer_class(page, many=True)
16-
return paginator.get_paginated_response(serializer.data)
17-
18-
serializer = serializer_class(queryset, many=True)
19+
def turn_off_pagination(data):
20+
serializer = _TurnOffPaginationSerializer(data=data)
21+
serializer.is_valid(raise_exception=True)
1922

20-
return Response(data=serializer.data)
23+
return serializer.validated_data["paginate"]
2124

2225

2326
class LimitOffsetPagination(_LimitOffsetPagination):
2427
default_limit = 10
2528
max_limit = 50
2629

27-
def get_count(self, queryset) -> int:
30+
def get_count(self, queryset: Union[QuerySet, Sequence]) -> int:
2831
"""
2932
Determine an object count, supporting either querysets or regular lists.
3033
"""
@@ -42,44 +45,52 @@ def get_count(self, queryset) -> int:
4245
return len(queryset)
4346

4447
def get_paginated_data(self, data):
45-
return OrderedDict([
46-
('limit', self.limit),
47-
('offset', self.offset),
48-
('count', self.count),
49-
('next', self.get_next_link()),
50-
('previous', self.get_previous_link()),
51-
('results', data)
52-
])
48+
return OrderedDict(
49+
[
50+
("limit", self.limit),
51+
("offset", self.offset),
52+
("count", self.count),
53+
("next", self.get_next_link()),
54+
("previous", self.get_previous_link()),
55+
("results", data),
56+
]
57+
)
5358

5459
def get_paginated_response(self, data):
5560
"""
5661
We redefine this method in order to return `limit` and `offset`.
5762
This is used by the frontend to construct the pagination itself.
5863
"""
59-
return Response(OrderedDict([
60-
('limit', self.limit),
61-
('offset', self.offset),
62-
('count', self.count),
63-
('next', self.get_next_link()),
64-
('previous', self.get_previous_link()),
65-
('results', data)
66-
]))
64+
return Response(
65+
OrderedDict(
66+
[
67+
("limit", self.limit),
68+
("offset", self.offset),
69+
("count", self.count),
70+
("next", self.get_next_link()),
71+
("previous", self.get_previous_link()),
72+
("results", data),
73+
]
74+
)
75+
)
6776

6877

6978
class CursorPagination(_CursorPagination):
7079
page_size = 50 # Return 50 items by default
7180

72-
def __init__(self, ordering):
81+
def __init__(self, ordering: Optional[str]):
7382
self.ordering: str = ordering or "-created_at"
7483

75-
def get_ordering(self, request, queryset, view):
84+
def get_ordering(
85+
self, request: Request, queryset: QuerySet, view: APIView
86+
) -> Tuple[str]:
7687
# The DRF CursorPagination expects the ordering as a tuple
7788
if isinstance(self.ordering, str):
7889
return (self.ordering,)
7990

8091
return tuple(self.ordering)
8192

82-
def _get_cursor(self, url):
93+
def _get_cursor(self, url: Optional[str]) -> Optional[str]:
8394
if not url:
8495
return None
8596

@@ -109,3 +120,42 @@ def get_paginated_response(self, data):
109120
]
110121
)
111122
)
123+
124+
125+
def _init_pagination_class(
126+
pagination_class: Type[BasePagination],
127+
ordering: Optional[str],
128+
) -> BasePagination:
129+
if isinstance(pagination_class, CursorPagination):
130+
return pagination_class(ordering=ordering)
131+
132+
return pagination_class()
133+
134+
135+
def response_paginate(
136+
*,
137+
pagination_class: Type[BasePagination],
138+
serializer_class: Type[serializers.Serializer],
139+
queryset: QuerySet,
140+
request: Request,
141+
view: APIView,
142+
ordering: Optional[str] = "-created_at"
143+
) -> Response:
144+
paginate = turn_off_pagination(data=request.GET)
145+
146+
if not paginate:
147+
data = serializer_class(queryset, many=True).data
148+
149+
return Response(data=data)
150+
151+
paginator = _init_pagination_class(pagination_class, ordering)
152+
153+
page = paginator.paginate_queryset(queryset, request, view=view)
154+
155+
if page is not None:
156+
serializer = serializer_class(page, many=True)
157+
return paginator.get_paginated_response(serializer.data)
158+
159+
serializer = serializer_class(queryset, many=True)
160+
161+
return Response(data=serializer.data)

styleguide_example/api/tests/pagination/test_get_paginated_response.py styleguide_example/api/tests/pagination/test_response_paginate.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from rest_framework.views import APIView
77
from rest_framework import serializers
88

9-
from styleguide_example.api.pagination import get_paginated_response, LimitOffsetPagination
9+
from styleguide_example.api.pagination import response_paginate, LimitOffsetPagination
1010

1111
from styleguide_example.users.services import user_create
1212
from styleguide_example.users.models import BaseUser
@@ -24,7 +24,7 @@ class Meta:
2424
def get(self, request):
2525
queryset = BaseUser.objects.order_by('id')
2626

27-
response = get_paginated_response(
27+
response = response_paginate(
2828
pagination_class=self.Pagination,
2929
serializer_class=self.OutputSerializer,
3030
queryset=queryset,
@@ -35,7 +35,7 @@ def get(self, request):
3535
return response
3636

3737

38-
class GetPaginatedResponseTests(TestCase):
38+
class ResponsePaginateTests(TestCase):
3939
def setUp(self):
4040
self.factory = APIRequestFactory()
4141

styleguide_example/users/apis.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from rest_framework.views import APIView
22
from rest_framework import serializers
33

4-
from styleguide_example.api.pagination import get_paginated_response, LimitOffsetPagination
4+
from styleguide_example.api.pagination import response_paginate, LimitOffsetPagination
55

66
from styleguide_example.users.selectors import user_list
77
from styleguide_example.users.models import BaseUser
@@ -34,7 +34,7 @@ def get(self, request):
3434

3535
users = user_list(filters=filters_serializer.validated_data)
3636

37-
return get_paginated_response(
37+
return response_paginate(
3838
pagination_class=self.Pagination,
3939
serializer_class=self.OutputSerializer,
4040
queryset=users,

0 commit comments

Comments
 (0)