[RFC PATCH v2 04/19] REST: Include 'first', 'last' refs in 'Link' header

Stephen Finucane stephen at that.guru
Thu Sep 2 02:57:41 AEST 2021


I've no idea why this wasn't done from day one, but it's a huge
usability win for anyone attempting to do pagination with this header.

Signed-off-by: Stephen Finucane <stephen at that.guru>
---
 patchwork/api/base.py | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git patchwork/api/base.py patchwork/api/base.py
index d870a511..7baac275 100644
--- patchwork/api/base.py
+++ patchwork/api/base.py
@@ -12,6 +12,7 @@ from rest_framework.pagination import PageNumberPagination
 from rest_framework.response import Response
 from rest_framework.serializers import HyperlinkedIdentityField
 from rest_framework.serializers import HyperlinkedModelSerializer
+from rest_framework.utils.urls import replace_query_param
 
 from patchwork.api import utils
 
@@ -59,19 +60,33 @@ class LinkHeaderPagination(PageNumberPagination):
     max_page_size = settings.MAX_REST_RESULTS_PER_PAGE
     page_size_query_param = 'per_page'
 
+    def get_first_link(self):
+        url = self.request.build_absolute_uri()
+        return replace_query_param(url, self.page_query_param, 1)
+
+    def get_last_link(self):
+        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_paginated_response(self, data):
         next_url = self.get_next_link()
         previous_url = self.get_previous_link()
+        first_url = self.get_first_link()
+        last_url = self.get_last_link()
+
+        links = []
+
+        if next_url is not None:
+            links.append(f'<{next_url}>; rel="next"')
+
+        if previous_url is not None:
+            links.append(f'<{previous_url}>; rel="prev"')
+
+        links.append(f'<{first_url}>; rel="first"')
+        links.append(f'<{last_url}>; rel="last"')
 
-        link = ''
-        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"'
-        link = link.format(next_url=next_url, previous_url=previous_url)
-        headers = {'Link': link} if link else {}
+        headers = {'Link': ', '.join(links)} if links else {}
         return Response(data, headers=headers)
 
 
-- 
2.31.1



More information about the Patchwork mailing list