Clean up pagination attributes

This commit is contained in:
Tom Christie 2015-03-04 15:51:00 +00:00
parent 5aa204e94f
commit 18cc0230bf
6 changed files with 71 additions and 32 deletions

View File

@ -84,10 +84,9 @@ The following attributes control the basic view behavior.
The following attributes are used to control pagination when used with list views. The following attributes are used to control pagination when used with list views.
* `paginate_by` - The size of pages to use with paginated data. If set to `None` then pagination is turned off. If unset this uses the same value as the `PAGINATE_BY` setting, which defaults to `None`. * `pagination_class` - The pagination class that should be used when paginating list results. Defaults to the same value as the `DEFAULT_PAGINATION_CLASS` setting, which is `'rest_framework.pagination.PageNumberPagination'`.
* `paginate_by_param` - The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If unset this uses the same value as the `PAGINATE_BY_PARAM` setting, which defaults to `None`.
* `pagination_serializer_class` - The pagination serializer class to use when determining the style of paginated responses. Defaults to the same value as the `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting. Note that usage of the `paginate_by`, `paginate_by_param` and `page_kwarg` attributes are now pending deprecation. The `pagination_serializer_class` attribute and `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting have been removed completely. Pagination settings should instead be controlled by overriding a pagination class and setting any configuration attributes there. See the pagination documentation for more details.
* `page_kwarg` - The name of a URL kwarg or URL query parameter which can be used by the client to control which page is requested. Defaults to `'page'`.
**Filtering**: **Filtering**:

View File

@ -141,7 +141,7 @@ The list views for users and code snippets could end up returning quite a lot of
We can change the default list style to use pagination, by modifying our `tutorial/settings.py` file slightly. Add the following setting: We can change the default list style to use pagination, by modifying our `tutorial/settings.py` file slightly. Add the following setting:
REST_FRAMEWORK = { REST_FRAMEWORK = {
'PAGINATE_BY': 10 'PAGE_SIZE': 10
} }
Note that settings in REST framework are all namespaced into a single dictionary setting, named 'REST_FRAMEWORK', which helps keep them well separated from your other project settings. Note that settings in REST framework are all namespaced into a single dictionary setting, named 'REST_FRAMEWORK', which helps keep them well separated from your other project settings.

View File

@ -123,7 +123,7 @@ We'd also like to set a few global settings. We'd like to turn on pagination, a
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',), 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'PAGINATE_BY': 10 'PAGE_SIZE': 10
} }
Okay, we're done. Okay, we're done.

View File

@ -18,6 +18,7 @@ from rest_framework.settings import api_settings
from rest_framework.utils.urls import ( from rest_framework.utils.urls import (
replace_query_param, remove_query_param replace_query_param, remove_query_param
) )
import warnings
def _positive_int(integer_string, strict=False, cutoff=None): def _positive_int(integer_string, strict=False, cutoff=None):
@ -203,18 +204,18 @@ class PageNumberPagination(BasePagination):
""" """
# The default page size. # The default page size.
# Defaults to `None`, meaning pagination is disabled. # Defaults to `None`, meaning pagination is disabled.
paginate_by = api_settings.PAGINATE_BY page_size = api_settings.PAGE_SIZE
# Client can control the page using this query parameter. # Client can control the page using this query parameter.
page_query_param = 'page' page_query_param = 'page'
# Client can control the page size using this query parameter. # Client can control the page size using this query parameter.
# Default is 'None'. Set to eg 'page_size' to enable usage. # Default is 'None'. Set to eg 'page_size' to enable usage.
paginate_by_param = api_settings.PAGINATE_BY_PARAM page_size_query_param = None
# Set to an integer to limit the maximum page size the client may request. # Set to an integer to limit the maximum page size the client may request.
# Only relevant if 'paginate_by_param' has also been set. # Only relevant if 'page_size_query_param' has also been set.
max_paginate_by = api_settings.MAX_PAGINATE_BY max_page_size = None
last_page_strings = ('last',) last_page_strings = ('last',)
@ -228,12 +229,48 @@ class PageNumberPagination(BasePagination):
attributes were set there. The attributes should now be set on attributes were set there. The attributes should now be set on
the pagination class, but the old style is still pending deprecation. the pagination class, but the old style is still pending deprecation.
""" """
for attr in ( assert not (
'paginate_by', 'page_query_param', getattr(view, 'pagination_serializer_class', None) or
'paginate_by_param', 'max_paginate_by' getattr(api_settings, 'DEFAULT_PAGINATION_SERIALIZER_CLASS', None)
), (
"The pagination_serializer_class attribute and "
"DEFAULT_PAGINATION_SERIALIZER_CLASS setting have been removed as "
"part of the 3.1 pagination API improvement. See the pagination "
"documentation for details on the new API."
)
for (settings_key, attr_name) in (
('PAGINATE_BY', 'page_size'),
('PAGINATE_BY_PARAM', 'page_size_query_param'),
('MAX_PAGINATE_BY', 'max_page_size')
): ):
if hasattr(view, attr): value = getattr(api_settings, settings_key, None)
setattr(self, attr, getattr(view, attr)) if value is not None:
setattr(self, attr_name, value)
warnings.warn(
"The `%s` settings key is pending deprecation. "
"Use the `%s` attribute on the pagination class instead." % (
settings_key, attr_name
),
PendingDeprecationWarning,
)
for (view_attr, attr_name) in (
('paginate_by', 'page_size'),
('page_query_param', 'page_query_param'),
('paginate_by_param', 'page_size_query_param'),
('max_paginate_by', 'max_page_size')
):
value = getattr(view, view_attr, None)
if value is not None:
setattr(self, attr_name, value)
warnings.warn(
"The `%s` view attribute is pending deprecation. "
"Use the `%s` attribute on the pagination class instead." % (
view_attr, attr_name
),
PendingDeprecationWarning,
)
def paginate_queryset(self, queryset, request, view=None): def paginate_queryset(self, queryset, request, view=None):
""" """
@ -264,7 +301,7 @@ class PageNumberPagination(BasePagination):
self.display_page_controls = True self.display_page_controls = True
self.request = request self.request = request
return self.page return list(self.page)
def get_paginated_response(self, data): def get_paginated_response(self, data):
return Response(OrderedDict([ return Response(OrderedDict([
@ -275,17 +312,17 @@ class PageNumberPagination(BasePagination):
])) ]))
def get_page_size(self, request): def get_page_size(self, request):
if self.paginate_by_param: if self.page_size_query_param:
try: try:
return _positive_int( return _positive_int(
request.query_params[self.paginate_by_param], request.query_params[self.page_size_query_param],
strict=True, strict=True,
cutoff=self.max_paginate_by cutoff=self.max_page_size
) )
except (KeyError, ValueError): except (KeyError, ValueError):
pass pass
return self.paginate_by return self.page_size
def get_next_link(self): def get_next_link(self):
if not self.page.has_next(): if not self.page.has_next():
@ -336,7 +373,7 @@ class LimitOffsetPagination(BasePagination):
http://api.example.org/accounts/?limit=100 http://api.example.org/accounts/?limit=100
http://api.example.org/accounts/?offset=400&limit=100 http://api.example.org/accounts/?offset=400&limit=100
""" """
default_limit = api_settings.PAGINATE_BY default_limit = api_settings.PAGE_SIZE
limit_query_param = 'limit' limit_query_param = 'limit'
offset_query_param = 'offset' offset_query_param = 'offset'
max_limit = None max_limit = None
@ -349,7 +386,7 @@ class LimitOffsetPagination(BasePagination):
self.request = request self.request = request
if self.count > self.limit and self.template is not None: if self.count > self.limit and self.template is not None:
self.display_page_controls = True self.display_page_controls = True
return queryset[self.offset:self.offset + self.limit] return list(queryset[self.offset:self.offset + self.limit])
def get_paginated_response(self, data): def get_paginated_response(self, data):
return Response(OrderedDict([ return Response(OrderedDict([
@ -440,7 +477,7 @@ class CursorPagination(BasePagination):
# Consider a max offset cap. # Consider a max offset cap.
# Tidy up the `get_ordering` API (eg remove queryset from it) # Tidy up the `get_ordering` API (eg remove queryset from it)
cursor_query_param = 'cursor' cursor_query_param = 'cursor'
page_size = api_settings.PAGINATE_BY page_size = api_settings.PAGE_SIZE
invalid_cursor_message = _('Invalid cursor') invalid_cursor_message = _('Invalid cursor')
ordering = None ordering = None
template = 'rest_framework/pagination/previous_and_next.html' template = 'rest_framework/pagination/previous_and_next.html'
@ -484,7 +521,7 @@ class CursorPagination(BasePagination):
# We also always fetch an extra item in order to determine if there is a # We also always fetch an extra item in order to determine if there is a
# page following on from this one. # page following on from this one.
results = list(queryset[offset:offset + self.page_size + 1]) results = list(queryset[offset:offset + self.page_size + 1])
self.page = results[:self.page_size] self.page = list(results[:self.page_size])
# Determine the position of the final item following the page. # Determine the position of the final item following the page.
if len(results) > len(self.page): if len(results) > len(self.page):

View File

@ -61,9 +61,7 @@ DEFAULTS = {
'NUM_PROXIES': None, 'NUM_PROXIES': None,
# Pagination # Pagination
'PAGINATE_BY': None, 'PAGE_SIZE': None,
'PAGINATE_BY_PARAM': None,
'MAX_PAGINATE_BY': None,
# Filtering # Filtering
'SEARCH_PARAM': 'search', 'SEARCH_PARAM': 'search',
@ -117,7 +115,12 @@ DEFAULTS = {
'UNICODE_JSON': True, 'UNICODE_JSON': True,
'COMPACT_JSON': True, 'COMPACT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True, 'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True 'UPLOADED_FILES_USE_URL': True,
# Pending deprecation:
'PAGINATE_BY': None,
'PAGINATE_BY_PARAM': None,
'MAX_PAGINATE_BY': None
} }

View File

@ -24,9 +24,9 @@ class TestPaginationIntegration:
return [item for item in queryset if item % 2 == 0] return [item for item in queryset if item % 2 == 0]
class BasicPagination(pagination.PageNumberPagination): class BasicPagination(pagination.PageNumberPagination):
paginate_by = 5 page_size = 5
paginate_by_param = 'page_size' page_size_query_param = 'page_size'
max_paginate_by = 20 max_page_size = 20
self.view = generics.ListAPIView.as_view( self.view = generics.ListAPIView.as_view(
serializer_class=PassThroughSerializer, serializer_class=PassThroughSerializer,
@ -185,7 +185,7 @@ class TestPageNumberPagination:
def setup(self): def setup(self):
class ExamplePagination(pagination.PageNumberPagination): class ExamplePagination(pagination.PageNumberPagination):
paginate_by = 5 page_size = 5
self.pagination = ExamplePagination() self.pagination = ExamplePagination()
self.queryset = range(1, 101) self.queryset = range(1, 101)