Browse Source

Allow subclasses of PagesAPIViewSet override default Page model

- Built on original PR #7372 and feedback on that solution
- Add unit tests and documentation
- Fix typo in API documentation for `CustomPagesAPIViewSet`
- Ensure that pk is used (not id) while filtering to prevent assumption that id is pk
Neeraj P Yetheendran 1 year ago
parent
commit
cdd6f1800c

+ 1 - 0
CHANGELOG.txt

@@ -29,6 +29,7 @@ Changelog
  * Add support for `placement` in the `human_readable_date` tooltip template tag (Rohit Sharma)
  * Add breadcrumbs to generic model views (Sage Abdullah)
  * Support passing extra context variables via the `{% component %}` tag (Matt Westcott)
+ * Allow subclasses of `PagesAPIViewSet` override default Page model via the `model` attribute (Neeraj Yetheendran, Herbert Poul)
  * Fix: Ensure that StreamField's `FieldBlock`s correctly set the `required` and `aria-describedby` attributes (Storm Heg)
  * Fix: Avoid an error when the moderation panel (admin dashboard) contains both snippets and private pages (Matt Westcott)
  * Fix: When deleting collections, ensure the collection name is correctly shown in the success message (LB (Ben) Johnston)

+ 1 - 0
CONTRIBUTORS.md

@@ -732,6 +732,7 @@
 * Faishal Manzar
 * Rohit Sharma
 * Subhajit Ghosh
+* Neeraj Yetheendran
 
 ## Translators
 

+ 15 - 1
docs/advanced_topics/api/v2/configuration.md

@@ -56,7 +56,21 @@ class CustomPagesAPIViewSet(PagesAPIViewSet):
     renderer_classes = [JSONRenderer]
     name = "pages"
 
-api_router.register_endpoint("pages", ProdPagesAPIViewSet)
+api_router.register_endpoint("pages", CustomPagesAPIViewSet)
+```
+
+Or changing the desired model to use for page results.
+
+```python
+from rest_framework.renderers import JSONRenderer
+
+# ...
+
+class PostPagesAPIViewSet(PagesAPIViewSet):
+    model = models.BlogPage
+    
+
+api_router.register_endpoint("posts", PostPagesAPIViewSet)
 ```
 
 Additionally, there is a base endpoint class you can use for adding different

+ 1 - 0
docs/releases/5.2.md

@@ -41,6 +41,7 @@ depth: 1
  * Add support for `placement` in `human_readable_date` the tooltip template tag (Rohit Sharma)
  * Add breadcrumbs to generic model views (Sage Abdullah)
  * Support passing extra context variables via the `{% component %}` tag (Matt Westcott)
+ * Allow subclasses of `PagesAPIViewSet` override default Page model via the `model` attribute (Neeraj Yetheendran, Herbert Poul)
 
 ### Bug fixes
 

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

@@ -1105,6 +1105,14 @@ class TestPageListingSearch(WagtailTestUtils, TransactionTestCase):
 
         self.assertEqual(set(page_id_list), {16, 18, 19})
 
+    def test_search_with_invalid_type(self):
+        # Check that a 400 error is returned when the type doesn't exist
+        response = self.get_response(type="demosite.InvalidPageType", search="blog")
+        content = json.loads(response.content.decode("UTF-8"))
+
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(content, {"message": "type doesn't exist"})
+
     def test_search_with_filter(self):
         response = self.get_response(
             title="Another blog post", search="blog", order="title"
@@ -1808,3 +1816,13 @@ class TestPageCacheInvalidation(TestCase):
         Page.objects.get(id=2).specific.save_revision()
 
         purge.assert_not_called()
+
+
+class TestPageViewSetSubclassing(PagesAPIViewSet):
+    model = models.BlogEntryPage
+
+    def test_get_queryset(self):
+        self.assertEqual(
+            self.get_queryset().model,
+            models.BlogEntryPage,
+        )

+ 9 - 5
wagtail/api/v2/views.py

@@ -555,20 +555,24 @@ class PagesAPIViewSet(BaseAPIViewSet):
 
         # Allow pages to be filtered to a specific type
         try:
-            models = page_models_from_string(
-                request.GET.get("type", "wagtailcore.Page")
-            )
+            models_type = request.GET.get("type", None)
+            models = models_type and page_models_from_string(models_type) or []
         except (LookupError, ValueError):
             raise BadRequestError("type doesn't exist")
 
         if not models:
-            return self.get_base_queryset()
+            if self.model == Page:
+                return self.get_base_queryset()
+            else:
+                return self.model.objects.filter(
+                    pk__in=self.get_base_queryset().values_list("pk", flat=True)
+                )
 
         elif len(models) == 1:
             # If a single page type has been specified, swap out the Page-based queryset for one based on
             # the specific page model so that we can filter on any custom APIFields defined on that model
             return models[0].objects.filter(
-                id__in=self.get_base_queryset().values_list("id", flat=True)
+                pk__in=self.get_base_queryset().values_list("pk", flat=True)
             )
 
         else:  # len(models) > 1