[Patchew-devel] [PATCH] rest: remove count from pagination output

Paolo Bonzini posted 1 patch 1 month, 3 weeks ago
api/{rest.py => rest/__init__.py} |  7 +++--
api/rest/pagination.py            | 49 +++++++++++++++++++++++++++++++
patchew/settings.py               |  2 +-
tests/test_diff.py                |  2 +-
tests/test_rest.py                | 28 +++++++++---------
5 files changed, 69 insertions(+), 19 deletions(-)
rename api/{rest.py => rest/__init__.py} (99%)
create mode 100644 api/rest/pagination.py
[Patchew-devel] [PATCH] rest: remove count from pagination output
Posted by Paolo Bonzini 1 month, 3 weeks ago
The same problem as b67dbe8 ("series-list: optimize generation of
page links", 2022-02-23) is also there in the REST output.  Remove the
overhead of the count query from REST API responses.
---
 api/{rest.py => rest/__init__.py} |  7 +++--
 api/rest/pagination.py            | 49 +++++++++++++++++++++++++++++++
 patchew/settings.py               |  2 +-
 tests/test_diff.py                |  2 +-
 tests/test_rest.py                | 28 +++++++++---------
 5 files changed, 69 insertions(+), 19 deletions(-)
 rename api/{rest.py => rest/__init__.py} (99%)
 create mode 100644 api/rest/pagination.py

diff --git a/api/rest.py b/api/rest/__init__.py
similarity index 99%
rename from api/rest.py
rename to api/rest/__init__.py
index 8117ddc..4a4b047 100644
--- a/api/rest.py
+++ b/api/rest/__init__.py
@@ -14,8 +14,8 @@ from django.http import Http404, HttpResponseRedirect
 from django.template import loader
 
 from mod import dispatch_module_hook
-from .models import Project, ProjectResult, Message, MessageResult, Result
-from .search import SearchEngine
+from ..models import Project, ProjectResult, Message, MessageResult, Result
+from ..search import SearchEngine
 from rest_framework import (
     permissions,
     serializers,
@@ -34,6 +34,7 @@ from rest_framework.fields import (
     EmailField,
     ListField,
 )
+from rest_framework.pagination import LimitOffsetPagination
 from rest_framework.relations import HyperlinkedIdentityField
 from rest_framework.response import Response
 from rest_framework.views import APIView
@@ -699,7 +700,7 @@ class MessagesViewSet(BaseMessageViewSet):
                 pass
         # Fake paginator response.  Note that there is no Location header.
         return Response(
-            OrderedDict([("count", len(results)), ("results", results)]),
+            OrderedDict([("results", results)]),
             status=status.HTTP_201_CREATED if results else status.HTTP_200_OK,
         )
 
diff --git a/api/rest/pagination.py b/api/rest/pagination.py
new file mode 100644
index 0000000..844ef6b
--- /dev/null
+++ b/api/rest/pagination.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 Red Hat, Inc.
+#
+# Authors:
+#     Paolo Bonzini <pbonzini@redhat.com>
+#
+# This work is licensed under the MIT License.  Please see the LICENSE file or
+# http://opensource.org/licenses/MIT.
+
+from rest_framework.pagination import LimitOffsetPagination
+from rest_framework.response import Response
+
+
+# remove count from paginator
+
+# This is referred to in settings.py, putting it with the rest of api/rest.py
+# results in a circular dependency between modules.
+
+
+class PatchewPagination(LimitOffsetPagination):
+    def get_paginated_response(self, data):
+        return Response(
+            {
+                "next": self.get_next_link(),
+                "previous": self.get_previous_link(),
+                "results": data,
+            }
+        )
+
+    def paginate_queryset(self, queryset, request, view=None):
+        self.offset = self.get_offset(request)
+        self.limit = self.get_limit(request)
+
+        # Get one extra element to check if there is a "next" page
+        q = list(queryset[self.offset : self.offset + self.limit + 1])
+        self.count = self.offset + len(q) if len(q) else self.offset - 1
+        q.pop()
+
+        self.request = request
+        if self.count > self.limit and self.template is not None:
+            self.display_page_controls = True
+
+        return q
+
+    def get_paginated_response_schema(self, schema):
+        ret = super(PatchewPagination, self).get_paginated_response_schema(schema)
+        del ret["properties"]["count"]
+        return ret
diff --git a/patchew/settings.py b/patchew/settings.py
index 8b19d19..0b8d695 100644
--- a/patchew/settings.py
+++ b/patchew/settings.py
@@ -70,7 +70,7 @@ REST_FRAMEWORK = {
         "rest_framework.authentication.TokenAuthentication",
         "rest_framework.authentication.SessionAuthentication",
     ),
-    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
+    "DEFAULT_PAGINATION_CLASS": "api.rest.pagination.PatchewPagination",
     "URL_FIELD_NAME": "resource_uri",
     "PAGE_SIZE": 50,
     "UPLOADED_FILES_USE_URL": True,
diff --git a/tests/test_diff.py b/tests/test_diff.py
index 6d3f311..dd74ec4 100755
--- a/tests/test_diff.py
+++ b/tests/test_diff.py
@@ -37,8 +37,8 @@ class DiffTest(PatchewTestCase):
         self.cli_import("0009-obsolete-by.mbox.gz")
 
         resp = self.api_client.get(self.REST_BASE + "series/?q=quorum")
-        self.assertEqual(resp.data["count"], 3)
         results = sorted(resp.data["results"], key=lambda y: y["version"])
+        self.assertEqual(len(results), 3)
         self.assertEqual(results[0]["version"], 1)
         self.assertEqual(results[1]["version"], 2)
         self.assertEqual(results[2]["version"], 3)
diff --git a/tests/test_rest.py b/tests/test_rest.py
index 9d96fa4..d7877f6 100755
--- a/tests/test_rest.py
+++ b/tests/test_rest.py
@@ -53,7 +53,7 @@ class RestTest(PatchewTestCase):
 
     def test_users(self):
         resp = self.api_client.get(self.REST_BASE + "users/")
-        self.assertEquals(resp.data["count"], 1)
+        self.assertEquals(len(resp.data["results"]), 1)
         self.assertEquals(resp.data["results"][0]["resource_uri"], self.USER_BASE)
         self.assertEquals(resp.data["results"][0]["username"], self.admin.username)
 
@@ -64,7 +64,7 @@ class RestTest(PatchewTestCase):
 
     def test_projects(self):
         resp = self.api_client.get(self.REST_BASE + "projects/")
-        self.assertEquals(resp.data["count"], 3)
+        self.assertEquals(len(resp.data["results"]), 3)
         self.assertEquals(resp.data["results"][0]["resource_uri"], self.PROJECT_BASE)
         self.assertEquals(resp.data["results"][0]["name"], "QEMU")
         self.assertEquals(
@@ -231,7 +231,7 @@ class RestTest(PatchewTestCase):
     def test_project_results_list(self):
         resp1 = self.api_client.get(self.PROJECT_BASE)
         resp = self.api_client.get(resp1.data["results"])
-        self.assertEqual(resp.data["count"], len(resp.data["results"]))
+        self.assertEqual(len(resp.data["results"]), len(resp.data["results"]))
 
     def test_series_single(self):
         resp = self.apply_and_retrieve(
@@ -371,10 +371,10 @@ class RestTest(PatchewTestCase):
         )
 
         resp = self.api_client.get(self.REST_BASE + "series/")
-        self.assertEqual(resp.data["count"], 2)
+        self.assertEqual(len(resp.data["results"]), 2)
 
         resp = self.api_client.get(self.PROJECT_BASE + "series/")
-        self.assertEqual(resp.data["count"], 2)
+        self.assertEqual(len(resp.data["results"]), 2)
 
         resp = self.api_client.get(self.REST_BASE + "projects/12345/series/")
         self.assertEqual(resp.status_code, 404)
@@ -386,7 +386,7 @@ class RestTest(PatchewTestCase):
             "20160628014747.20971-1-famz@redhat.com",
         )
         resp = self.api_client.get(resp1.data["results"])
-        self.assertEqual(resp.data["count"], len(resp.data["results"]))
+        self.assertEqual(len(resp.data["results"]), len(resp.data["results"]))
 
     def test_series_search(self):
         resp1 = self.apply_and_retrieve(
@@ -401,7 +401,7 @@ class RestTest(PatchewTestCase):
         )
 
         resp = self.api_client.get(self.REST_BASE + "series/?q=quorum")
-        self.assertEqual(resp.data["count"], 1)
+        self.assertEqual(len(resp.data["results"]), 1)
         self.assertEqual(
             resp.data["results"][0]["resource_uri"], resp2.data["resource_uri"]
         )
@@ -410,7 +410,7 @@ class RestTest(PatchewTestCase):
         self.assertEqual("patches" in resp.data["results"][0], False)
 
         resp = self.api_client.get(self.REST_BASE + "series/?q=project:QEMU")
-        self.assertEqual(resp.data["count"], 2)
+        self.assertEqual(len(resp.data["results"]), 2)
 
         def cmp_result(a, expected):
             self.assertEqual(a["resource_uri"], expected["resource_uri"])
@@ -579,7 +579,7 @@ class RestTest(PatchewTestCase):
             self.REST_BASE + "messages/", data, content_type="application/json"
         )
         self.assertEqual(resp.status_code, 201)
-        self.assertEqual(resp.data["count"], 2)
+        self.assertEqual(len(resp.data["results"]), 2)
         resp_get = self.api_client.get(
             self.PROJECT_BASE
             + "messages/20180223132311.26555-2-marcandre.lureau@redhat.com/"
@@ -604,7 +604,7 @@ class RestTest(PatchewTestCase):
             self.REST_BASE + "messages/", data, content_type="message/rfc822"
         )
         self.assertEqual(resp.status_code, 201)
-        self.assertEqual(resp.data["count"], 2)
+        self.assertEqual(len(resp.data["results"]), 2)
         resp_get = self.api_client.get(
             self.PROJECT_BASE
             + "messages/20180223132311.26555-2-marcandre.lureau@redhat.com/"
@@ -639,7 +639,7 @@ class RestTest(PatchewTestCase):
             self.REST_BASE + "messages/", data, content_type="message/rfc822"
         )
         self.assertEqual(resp.status_code, 200)
-        self.assertEqual(resp.data["count"], 0)
+        self.assertEqual(len(resp.data["results"]), 0)
         resp_get = self.api_client.get(
             self.PROJECT_BASE
             + "messages/20180223132311.26555-2-marcandre.lureau@redhat.com/"
@@ -662,7 +662,7 @@ class RestTest(PatchewTestCase):
             self.REST_BASE + "messages/", data, content_type="message/rfc822"
         )
         self.assertEqual(resp.status_code, 201)
-        self.assertEqual(resp.data["count"], 1)
+        self.assertEqual(len(resp.data["results"]), 1)
         resp_get = self.api_client.get(
             self.PROJECT_BASE
             + "messages/20180223132311.26555-2-marcandre.lureau@redhat.com/"
@@ -684,7 +684,7 @@ class RestTest(PatchewTestCase):
             self.REST_BASE + "messages/", data, content_type="message/rfc822"
         )
         self.assertEqual(resp.status_code, 201)
-        self.assertEqual(resp.data["count"], 2)
+        self.assertEqual(len(resp.data["results"]), 2)
         resp_get = self.api_client.get(
             self.PROJECT_BASE
             + "messages/20180223132311.26555-2-marcandre.lureau@redhat.com/"
@@ -754,7 +754,7 @@ class RestTest(PatchewTestCase):
 
         message = series.data["message"]
         resp = self.api_client.get(message + "replies/")
-        self.assertEqual(resp.data["count"], 4)
+        self.assertEqual(len(resp.data["results"]), 4)
         self.assertEqual(
             resp.data["results"][0]["resource_uri"],
             self.PROJECT_BASE
-- 
2.35.1

_______________________________________________
Patchew-devel mailing list
Patchew-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/patchew-devel