2
0
Эх сурвалжийг харах

API - Allow ordering by multiple fields

- Closes #7429
- Builds on original PR #7433 (Jake Howard)
rohitsrma 1 жил өмнө
parent
commit
15642124b6

+ 1 - 0
CHANGELOG.txt

@@ -509,6 +509,7 @@ Changelog
  * Delay hiding the contents of the side panels when closing, so the animation is smoother (Thibaud Colas)
  * ListBlock now shows item-by-item differences when comparing versions (Tidiane Dia)
  * Implement a new design for chooser buttons with better accessibility (Thibaud Colas)
+ * API ordering now supports multiple fields (Rohit Sharma, Jake Howard)
  * Fix: Make sure workflow timeline icons are visible in high-contrast mode (Loveth Omokaro)
  * Fix: Ensure authentication forms (login, password reset) have a visible border in Windows high-contrast mode (Loveth Omokaro)
  * Fix: Ensure visual consistency between buttons and links as buttons in Windows high-contrast mode (Albina Starykova)

+ 28 - 0
docs/advanced_topics/api/v2/usage.md

@@ -1,3 +1,5 @@
+(api_v2_usage)=
+
 # Wagtail API v2 Usage Guide
 
 The Wagtail API module exposes a public, read only, JSON-formatted API which
@@ -162,6 +164,8 @@ either a number (the new maximum value) or `None` (which disables maximum
 value check).
 ```
 
+(api_v2_usage_ordering)=
+
 ### Ordering
 
 The results can be ordered by any field by setting the `?order` parameter to
@@ -207,6 +211,30 @@ Ordering is case-sensitive so lowercase letters are always ordered after
 uppercase letters when in ascending order.
 ```
 
+#### Multiple ordering
+
+Multiple fields cab be passed into the `?order` for consecutive ordering.
+
+```
+GET /api/v2/pages/?order=title,-slug
+
+HTTP 200 OK
+Content-Type: application/json
+
+{
+    "meta": {
+        "total_count": 50
+    },
+    "items": [
+        pages will be ordered by title and for all matching titles (a-z), then sorted by slug (z-a).
+    ]
+}
+```
+
+```{versionadded} 5.2
+
+```
+
 #### Random ordering
 
 Passing `random` into the `?order` parameter will make results return in a

+ 1 - 0
docs/releases/5.2.md

@@ -51,6 +51,7 @@ depth: 1
  * Add the ability to define listing buttons on generic `IndexView` (Sage Abdullah)
  * Add a visual progress bar to the output of the `wagtail_update_image_renditions` management command (Faishal Manzar)
  * Increase the read buffer size to improve efficiency and performance when generating file hashes for document or image uploads, use `hashlib.file_digest` if available (Python 3.11+) (Jake Howard)
+ * API ordering now [supports multiple fields](api_v2_usage_ordering) (Rohit Sharma, Jake Howard)
 
 ### Bug fixes
 

+ 30 - 19
wagtail/api/v2/filters.py

@@ -63,8 +63,8 @@ class FieldsFilter(BaseFilterBackend):
 class OrderingFilter(BaseFilterBackend):
     def filter_queryset(self, request, queryset, view):
         """
-        This applies ordering to the result set
-        Eg: ?order=title
+        This applies ordering to the result set with support for multiple fields.
+        Eg: ?order=title or ?order=title,created_at
 
         It also supports reverse ordering
         Eg: ?order=-title
@@ -73,10 +73,14 @@ class OrderingFilter(BaseFilterBackend):
         Eg: ?order=random
         """
         if "order" in request.GET:
-            order_by = request.GET["order"]
+            order_by_list = request.GET["order"].split(",")
 
             # Random ordering
-            if order_by == "random":
+            if "random" in order_by_list:
+                if len(order_by_list) > 1:
+                    raise BadRequestError(
+                        "random ordering cannot be combined with other fields"
+                    )
                 # Prevent ordering by random with offset
                 if "offset" in request.GET:
                     raise BadRequestError(
@@ -85,21 +89,28 @@ class OrderingFilter(BaseFilterBackend):
 
                 return queryset.order_by("?")
 
-            # Check if reverse ordering is set
-            if order_by.startswith("-"):
-                reverse_order = True
-                order_by = order_by[1:]
-            else:
-                reverse_order = False
-
-            # Add ordering
-            if order_by in view.get_available_fields(queryset.model):
-                queryset = queryset.order_by(order_by)
-            else:
-                # Unknown field
-                raise BadRequestError("cannot order by '%s' (unknown field)" % order_by)
-
-            # Reverse order
+            order_by_fields = []
+            for order_by in order_by_list:
+                # Check if reverse ordering is set
+                if order_by.startswith("-"):
+                    reverse_order = True
+                    order_by = order_by[1:]
+                else:
+                    reverse_order = False
+
+                # Add ordering
+                if order_by in view.get_available_fields(queryset.model):
+                    order_by_fields.append(order_by)
+                else:
+                    # Unknown field
+                    raise BadRequestError(
+                        "cannot order by '%s' (unknown field)" % order_by
+                    )
+
+            # Apply ordering to the queryset
+            queryset = queryset.order_by(*order_by_fields)
+
+            # Reverse order if needed
             if reverse_order:
                 queryset = queryset.reverse()
 

+ 65 - 0
wagtail/api/v2/tests/test_pages.py

@@ -941,6 +941,71 @@ class TestPageListing(WagtailTestUtils, TestCase):
             content, {"message": "cannot order by 'not_a_field' (unknown field)"}
         )
 
+    def test_random_ordering_with_unknown_field_gives_error(self):
+        response = self.get_response(order=["random,id"])
+        content = json.loads(response.content.decode("UTF-8"))
+
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(
+            content, {"message": "random ordering cannot be combined with other fields"}
+        )
+
+    def test_ordering_by_id_and_slug(self):
+        response = self.get_response(order=["id,slug"])
+        content = json.loads(response.content.decode("UTF-8"))
+
+        page_id_list = self.get_page_id_list(content)
+        expected_order = [
+            2,
+            4,
+            5,
+            6,
+            8,
+            9,
+            10,
+            12,
+            13,
+            14,
+            15,
+            16,
+            17,
+            18,
+            19,
+            20,
+            21,
+            22,
+            23,
+        ]
+        self.assertEqual(page_id_list[:15], expected_order[:15])
+
+    def test_ordering_by_title_and_id_backwards(self):
+        response = self.get_response(order=["title,-id"])
+        content = json.loads(response.content.decode("UTF-8"))
+
+        page_id_list = self.get_page_id_list(content)
+        expected_order = [
+            15,
+            10,
+            6,
+            17,
+            20,
+            13,
+            2,
+            4,
+            9,
+            8,
+            14,
+            12,
+            18,
+            16,
+            5,
+            23,
+            19,
+            22,
+            21,
+        ]
+        self.assertEqual(page_id_list[:5], expected_order[:5])
+
     # LIMIT
 
     def test_limit_only_two_items_returned(self):