The DRF docs state that:
Pagination is only performed automatically if you're using the generic views or viewsets.
But I'm using a ModelViewSet
which inherits from ViewSet
, so I tell myself "Cool, all I have to do is add this to my settings.py
":
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 12, # for 12-col grid css frameworks
However, this didn't work.
If I send a GET request for 27 items it returns all of them (in both the browsable API and json).
- Am I right in thinking I should only be returned 12?
- Sub-question: PAGE_SIZE is the number of top-level objects returned per page, right? I saw a few examples with
PAGINATE_BY
but it
doesn't feature in the source so I presume it's deprecated?
I'm using DRF 3.6.3, django 1.11.2.
Edit: my settings:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 12,
}
I've also added pagination_class = PageNumberPagination
to the ModelViewSet
class, without effect.
Here's the verification from the shell that the Pagination class does know the page size it's supposed to provide:
>>> from rest_framework.pagination import PageNumberPagination
>>> p = PageNumberPagination()
>>> p.max_page_size
>>> print(p.page_size)
12
It is true that pagination doesn't work by default on a ModelViewSet
despite inheriting from GenericViewSet
and the ListMixin
. You need to add it manually:
I have composed a Q&A style example which attends that problem and have tested it on a ModelViewSet
class.
I will summarize it a bit:
Create a custom mixin to utilize the pagination_class
:
class MyPaginationMixin(object):
@property
def paginator(self):
"""
The paginator instance associated with the view, or `None`.
"""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
def paginate_queryset(self, queryset):
"""
Return a single page of results, or `None` if pagination
is disabled.
"""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
def get_paginated_response(self, data):
"""
Return a paginated style `Response` object for the given
output data.
"""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
Make your viewset extend that mixin and override the list()
method of the ModelViewSet
:
class MyViewset(MyPaginationMixin, viewsets.ModelViewSet):
# since you are setting pagination on the settings, use this:
pagination_class = settings.DEFAULT_PAGINATION_CLASS
def list(self, request, *args, **kwargs):
response = dict(
super(MyViewSet, self).list(self, *args, **kwargs).data)
page = self.paginate_queryset(response['results'])
if page is not None:
serializer = self.serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
else:
# Something went wrong here...
You need to calibrate this solution to your needs of course, but this will add pagination to a ModelViewSet
.
For the subquestion, @Linovia 's comment is correct, PAGINATE_BY
is deprecated and PAGE_SIZE
is the current setting for the response page size.
It should work.
If it doesn't and you don't have typo, look at the server logs and you may find something telling you that pagination without order will not work:
xxxx/django/core/paginator.py:112: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: ....
In order to fix that make sure you specify an ordering in the model's Meta or set an order_by
within ModelViewSet
's queryset
.