[RFC] REST: Use cursor-based pagination for '/events'
Stephen Finucane
stephen at that.guru
Thu Jun 22 06:44:10 AEST 2017
Given the high-volume, time-based nature of this API, cursor-based
pagination provides some important usability improvements over page
number-based pagination, most notably in going back through historical
pages.
Signed-off-by: Stephen Finucane <stephen at that.guru>
Cc: Aaron Conole <aconole at bytheb.org>
---
patchwork/api/base.py | 30 +++++++++++++++++++++++-------
patchwork/api/event.py | 4 +++-
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/patchwork/api/base.py b/patchwork/api/base.py
index 09b3bef..958dfc3 100644
--- a/patchwork/api/base.py
+++ b/patchwork/api/base.py
@@ -19,22 +19,20 @@
from django.conf import settings
from django.shortcuts import get_object_or_404
+from rest_framework import pagination
from rest_framework import permissions
-from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.serializers import HyperlinkedIdentityField
-class LinkHeaderPagination(PageNumberPagination):
- """Provide pagination based on rfc5988.
+class LinkHeaderMixin(object):
+ """Provide pagination based on RFC5988.
This is the Link header, similar to how GitHub does it. See:
- https://tools.ietf.org/html/rfc5988#section-5
- https://developer.github.com/guides/traversing-with-pagination
+ - https://tools.ietf.org/html/rfc5988#section-5
+ - https://developer.github.com/guides/traversing-with-pagination
"""
- page_size = max_page_size = settings.REST_RESULTS_PER_PAGE
- page_size_query_param = 'per_page'
def get_paginated_response(self, data):
next_url = self.get_next_link()
@@ -47,11 +45,29 @@ class LinkHeaderPagination(PageNumberPagination):
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 {}
+
return Response(data, headers=headers)
+
+class PageNumberPagination(LinkHeaderMixin, pagination.PageNumberPagination):
+ """RFC5988-based page-number pagination."""
+ page_size = max_page_size = settings.REST_RESULTS_PER_PAGE
+ page_size_query_param = 'per_page'
+
+
+class CursorPagination(LinkHeaderMixin, pagination.CursorPagination):
+ """RFC5988-based cursor pagination."""
+ page_size = 30
+ cursor_query_param = 'cursor'
+
+
+LinkHeaderPagination = PageNumberPagination
+
+
class PatchworkPermission(permissions.BasePermission):
"""This permission works for Project and Patch model objects"""
def has_object_permission(self, request, view, obj):
diff --git a/patchwork/api/event.py b/patchwork/api/event.py
index cc9270a..d333501 100644
--- a/patchwork/api/event.py
+++ b/patchwork/api/event.py
@@ -23,6 +23,7 @@ from rest_framework.generics import ListAPIView
from rest_framework.serializers import ModelSerializer
from rest_framework.serializers import SerializerMethodField
+from patchwork.api.base import CursorPagination
from patchwork.api.embedded import CheckSerializer
from patchwork.api.embedded import CoverLetterSerializer
from patchwork.api.embedded import PatchSerializer
@@ -34,6 +35,7 @@ from patchwork.api.patch import StateField
from patchwork.models import Event
+
class EventSerializer(ModelSerializer):
project = ProjectSerializer(read_only=True)
@@ -89,8 +91,8 @@ class EventList(ListAPIView):
"""List events."""
serializer_class = EventSerializer
+ pagination_class = CursorPagination
filter_class = EventFilter
- page_size_query_param = None # fixed page size
ordering = '-date'
ordering_fields = ()
--
2.9.4
More information about the Patchwork
mailing list