This commit is contained in:
Steve Lacey 2015-03-02 09:18:06 +00:00
commit 15a212945e
2 changed files with 78 additions and 39 deletions

View File

@ -21,9 +21,11 @@ Pagination is only performed automatically if you're using the generic views or
The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example, to use the built-in limit/offset pagination, you would do: The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example, to use the built-in limit/offset pagination, you would do:
REST_FRAMEWORK = { ```py
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination' REST_FRAMEWORK = {
} 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
}
```
You can also set the pagination class on an individual view by using the `pagination_class` attribute. Typically you'll want to use the same pagination style throughout your API, although you might want to vary individual aspects of the pagination, such as default or maximum page size, on a per-view basis. You can also set the pagination class on an individual view by using the `pagination_class` attribute. Typically you'll want to use the same pagination style throughout your API, although you might want to vary individual aspects of the pagination, such as default or maximum page size, on a per-view basis.
@ -31,27 +33,34 @@ You can also set the pagination class on an individual view by using the `pagina
If you want to modify particular aspects of the pagination style, you'll want to override one of the pagination classes, and set the attributes that you want to change. If you want to modify particular aspects of the pagination style, you'll want to override one of the pagination classes, and set the attributes that you want to change.
class LargeResultsSetPagination(PageNumberPagination): ```py
paginate_by = 1000 class LargeResultsSetPagination(PageNumberPagination):
paginate_by_param = 'page_size' paginate_by = 1000
max_paginate_by = 10000 paginate_by_param = 'page_size'
max_paginate_by = 10000
class StandardResultsSetPagination(PageNumberPagination): class StandardResultsSetPagination(PageNumberPagination):
paginate_by = 100 paginate_by = 100
paginate_by_param = 'page_size' paginate_by_param = 'page_size'
max_paginate_by = 1000 max_paginate_by = 1000
```
You can then apply your new style to a view using the `.pagination_class` attribute: You can then apply your new style to a view using the `.pagination_class` attribute:
class BillingRecordsView(generics.ListAPIView): ```py
queryset = Billing.objects.all() class BillingRecordsView(generics.ListAPIView):
serializer = BillingRecordsSerializer queryset = Billing.objects.all()
pagination_class = LargeResultsSetPagination serializer = BillingRecordsSerializer
pagination_class = LargeResultsSetPagination
```
Or apply the style globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example: Or apply the style globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example:
REST_FRAMEWORK = { ```py
'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination' } REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}
```
--- ---
@ -84,32 +93,49 @@ Note that the `paginate_queryset` method may set state on the pagination instanc
Let's modify the built-in `PageNumberPagination` style, so that instead of include the pagination links in the body of the response, we'll instead include a `Link` header, in a [similar style to the GitHub API][github-link-pagination]. Let's modify the built-in `PageNumberPagination` style, so that instead of include the pagination links in the body of the response, we'll instead include a `Link` header, in a [similar style to the GitHub API][github-link-pagination].
class LinkHeaderPagination(pagination.PageNumberPagination): ```py
def get_paginated_response(self, data): from rest_framework.pagination import PageNumberPagination
next_url = self.get_next_link() previous_url = self.get_previous_link() from rest_framework.response import Response
if next_url is not None and previous_url is not None:
link = '<{next_url}; rel="next">, <{previous_url}; rel="prev">'
elif next_url is not None:
link = '<{next_url}; rel="next">'
elif previous_url is not None:
link = '<{previous_url}; rel="prev">'
else:
link = ''
link = link.format(next_url=next_url, previous_url=previous_url) class LinkHeaderPagination(PageNumberPagination):
headers = {'Link': link} if link else {} def get_paginated_response(self, data):
headers = {
'X-Page': self.page.number,
'X-Per-Page': self.page.paginator.per_page,
'X-Total': self.page.paginator.count,
'X-Total-Pages': self.page.paginator.num_pages
}
return Response(data, headers=headers) first_url = self.get_first_link()
prev_url = self.get_previous_link()
next_url = self.get_next_link()
last_url = self.get_last_link()
link = '<{}>; rel="{}"'
links = []
if first_url: links.append(link.format(first_url, 'first'))
if prev_url: links.append(link.format(prev_url, 'prev'))
if next_url: links.append(link.format(next_url, 'next'))
if last_url: links.append(link.format(last_url, 'last'))
if links:
headers['Link'] = ", ".join(links)
return Response(data, headers=headers)
```
## Using your custom pagination class ## Using your custom pagination class
To have your custom pagination class be used by default, use the `DEFAULT_PAGINATION_CLASS` setting: To have your custom pagination class be used by default, use the `DEFAULT_PAGINATION_CLASS` setting:
REST_FRAMEWORK = { ```py
'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination', REST_FRAMEWORK = {
'PAGINATE_BY': 10 'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination',
} 'PAGINATE_BY': 10
}
```
API responses for list endpoints will now include a `Link` header, instead of including the pagination links as part of the body of the response, for example: API responses for list endpoints will now include a `Link` header, instead of including the pagination links as part of the body of the response, for example:

View File

@ -287,12 +287,11 @@ class PageNumberPagination(BasePagination):
return self.paginate_by return self.paginate_by
def get_next_link(self): def get_first_link(self):
if not self.page.has_next(): if not self.page.has_previous():
return None return None
url = self.request.build_absolute_uri() url = self.request.build_absolute_uri()
page_number = self.page.next_page_number() return remove_query_param(url, self.page_query_param)
return replace_query_param(url, self.page_query_param, page_number)
def get_previous_link(self): def get_previous_link(self):
if not self.page.has_previous(): if not self.page.has_previous():
@ -303,6 +302,20 @@ class PageNumberPagination(BasePagination):
return remove_query_param(url, self.page_query_param) return remove_query_param(url, self.page_query_param)
return replace_query_param(url, self.page_query_param, page_number) return replace_query_param(url, self.page_query_param, page_number)
def get_next_link(self):
if not self.page.has_next():
return None
url = self.request.build_absolute_uri()
page_number = self.page.next_page_number()
return replace_query_param(url, self.page_query_param, page_number)
def get_last_link(self):
if not self.page.has_next():
return None
url = self.request.build_absolute_uri()
page_number = self.page.paginator.num_pages
return replace_query_param(url, self.page_query_param, page_number)
def get_html_context(self): def get_html_context(self):
base_url = self.request.build_absolute_uri() base_url = self.request.build_absolute_uri()