diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 61c8e8d88..39e09aaa5 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -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. -* `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`. -* `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. -* `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'`. +* `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'`. + +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. **Filtering**: diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 740a4ce21..91cdd6f10 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -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: 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. diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index a4474c34e..fe0ecbc7e 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -123,7 +123,7 @@ We'd also like to set a few global settings. We'd like to turn on pagination, a REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',), - 'PAGINATE_BY': 10 + 'PAGE_SIZE': 10 } Okay, we're done. diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 809858737..6a2f5b271 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -18,6 +18,7 @@ from rest_framework.settings import api_settings from rest_framework.utils.urls import ( replace_query_param, remove_query_param ) +import warnings def _positive_int(integer_string, strict=False, cutoff=None): @@ -203,18 +204,18 @@ class PageNumberPagination(BasePagination): """ # The default page size. # 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. page_query_param = 'page' # Client can control the page size using this query parameter. # 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. - # Only relevant if 'paginate_by_param' has also been set. - max_paginate_by = api_settings.MAX_PAGINATE_BY + # Only relevant if 'page_size_query_param' has also been set. + max_page_size = None last_page_strings = ('last',) @@ -228,12 +229,48 @@ class PageNumberPagination(BasePagination): attributes were set there. The attributes should now be set on the pagination class, but the old style is still pending deprecation. """ - for attr in ( - 'paginate_by', 'page_query_param', - 'paginate_by_param', 'max_paginate_by' + assert not ( + getattr(view, 'pagination_serializer_class', None) or + 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): - setattr(self, attr, getattr(view, attr)) + value = getattr(api_settings, settings_key, None) + 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): """ @@ -264,7 +301,7 @@ class PageNumberPagination(BasePagination): self.display_page_controls = True self.request = request - return self.page + return list(self.page) def get_paginated_response(self, data): return Response(OrderedDict([ @@ -275,17 +312,17 @@ class PageNumberPagination(BasePagination): ])) def get_page_size(self, request): - if self.paginate_by_param: + if self.page_size_query_param: try: return _positive_int( - request.query_params[self.paginate_by_param], + request.query_params[self.page_size_query_param], strict=True, - cutoff=self.max_paginate_by + cutoff=self.max_page_size ) except (KeyError, ValueError): pass - return self.paginate_by + return self.page_size def get_next_link(self): 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/?offset=400&limit=100 """ - default_limit = api_settings.PAGINATE_BY + default_limit = api_settings.PAGE_SIZE limit_query_param = 'limit' offset_query_param = 'offset' max_limit = None @@ -349,7 +386,7 @@ class LimitOffsetPagination(BasePagination): self.request = request if self.count > self.limit and self.template is not None: 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): return Response(OrderedDict([ @@ -440,7 +477,7 @@ class CursorPagination(BasePagination): # Consider a max offset cap. # Tidy up the `get_ordering` API (eg remove queryset from it) cursor_query_param = 'cursor' - page_size = api_settings.PAGINATE_BY + page_size = api_settings.PAGE_SIZE invalid_cursor_message = _('Invalid cursor') ordering = None 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 # page following on from this one. 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. if len(results) > len(self.page): diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 394b12622..a3e9f5902 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -61,9 +61,7 @@ DEFAULTS = { 'NUM_PROXIES': None, # Pagination - 'PAGINATE_BY': None, - 'PAGINATE_BY_PARAM': None, - 'MAX_PAGINATE_BY': None, + 'PAGE_SIZE': None, # Filtering 'SEARCH_PARAM': 'search', @@ -117,7 +115,12 @@ DEFAULTS = { 'UNICODE_JSON': True, 'COMPACT_JSON': 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 } diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 13bfb6272..6b39a6f22 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -24,9 +24,9 @@ class TestPaginationIntegration: return [item for item in queryset if item % 2 == 0] class BasicPagination(pagination.PageNumberPagination): - paginate_by = 5 - paginate_by_param = 'page_size' - max_paginate_by = 20 + page_size = 5 + page_size_query_param = 'page_size' + max_page_size = 20 self.view = generics.ListAPIView.as_view( serializer_class=PassThroughSerializer, @@ -185,7 +185,7 @@ class TestPageNumberPagination: def setup(self): class ExamplePagination(pagination.PageNumberPagination): - paginate_by = 5 + page_size = 5 self.pagination = ExamplePagination() self.queryset = range(1, 101)