diff --git a/wagtail/admin/api/endpoints.py b/wagtail/admin/api/endpoints.py index ba9206df65..ea278141f0 100644 --- a/wagtail/admin/api/endpoints.py +++ b/wagtail/admin/api/endpoints.py @@ -35,6 +35,7 @@ class PagesAdminAPIEndpoint(PagesAPIEndpoint): 'children', 'descendants', 'parent', + 'ancestors', ] body_fields = PagesAPIEndpoint.body_fields + [ diff --git a/wagtail/admin/api/serializers.py b/wagtail/admin/api/serializers.py index 40e43cd0c5..589257148c 100644 --- a/wagtail/admin/api/serializers.py +++ b/wagtail/admin/api/serializers.py @@ -2,7 +2,7 @@ from collections import OrderedDict from rest_framework.fields import Field, ReadOnlyField -from wagtail.api.v2.serializers import PageSerializer +from wagtail.api.v2.serializers import PageSerializer, get_serializer_class from wagtail.api.v2.utils import get_full_url from wagtail.core.models import Page @@ -76,8 +76,42 @@ class PageDescendantsField(Field): ]) +class PageAncestorsField(Field): + """ + Serializes the page's ancestry. + + Example: + "ancestry": [ + { + "id": 1, + "meta": { + "type": "wagtailcore.Page", + "detail_url": "/api/v1/pages/1/" + }, + "title": "Root" + }, + { + "id": 2, + "meta": { + "type": "home.HomePage", + "detail_url": "/api/v1/pages/2/" + }, + "title": "Home" + } + ] + """ + def get_attribute(self, instance): + return instance + + def to_representation(self, page): + serializer_class = get_serializer_class(Page, ['id', 'type', 'detail_url', 'html_url', 'title', 'admin_display_title'], meta_fields=['type', 'detail_url', 'html_url'], base=AdminPageSerializer) + serializer = serializer_class(context=self.context, many=True) + return serializer.to_representation(page.get_ancestors()) + + class AdminPageSerializer(PageSerializer): status = PageStatusField(read_only=True) children = PageChildrenField(read_only=True) descendants = PageDescendantsField(read_only=True) + ancestors = PageAncestorsField(read_only=True) admin_display_title = ReadOnlyField(source='get_admin_display_title') diff --git a/wagtail/admin/tests/api/test_pages.py b/wagtail/admin/tests/api/test_pages.py index 95566836e9..cc67c6ac48 100644 --- a/wagtail/admin/tests/api/test_pages.py +++ b/wagtail/admin/tests/api/test_pages.py @@ -157,7 +157,7 @@ class TestAdminPageListing(AdminAPITestCase, TestPageListing): for page in content['items']: self.assertEqual(set(page.keys()), {'id', 'meta', 'title', 'admin_display_title', 'date', 'related_links', 'tags', 'carousel_items', 'body', 'feed_image', 'feed_image_thumbnail'}) - self.assertEqual(set(page['meta'].keys()), {'type', 'detail_url', 'show_in_menus', 'first_published_at', 'seo_title', 'slug', 'parent', 'html_url', 'search_description', 'children', 'descendants', 'status', 'latest_revision_created_at'}) + self.assertEqual(set(page['meta'].keys()), {'type', 'detail_url', 'show_in_menus', 'first_published_at', 'seo_title', 'slug', 'parent', 'html_url', 'search_description', 'children', 'descendants', 'ancestors', 'status', 'latest_revision_created_at'}) def test_all_fields_then_remove_something(self): response = self.get_response(type='demosite.BlogEntryPage', fields='*,-title,-admin_display_title,-date,-seo_title,-status') @@ -165,7 +165,7 @@ class TestAdminPageListing(AdminAPITestCase, TestPageListing): for page in content['items']: self.assertEqual(set(page.keys()), {'id', 'meta', 'related_links', 'tags', 'carousel_items', 'body', 'feed_image', 'feed_image_thumbnail'}) - self.assertEqual(set(page['meta'].keys()), {'type', 'detail_url', 'show_in_menus', 'first_published_at', 'slug', 'parent', 'html_url', 'search_description', 'children', 'descendants', 'latest_revision_created_at'}) + self.assertEqual(set(page['meta'].keys()), {'type', 'detail_url', 'show_in_menus', 'first_published_at', 'slug', 'parent', 'html_url', 'search_description', 'children', 'descendants', 'ancestors', 'latest_revision_created_at'}) def test_all_nested_fields(self): response = self.get_response(type='demosite.BlogEntryPage', fields='feed_image(*)') @@ -507,6 +507,8 @@ class TestAdminPageDetail(AdminAPITestCase, TestPageDetail): # Check the type info self.assertIsInstance(content['__types'], dict) self.assertEqual(set(content['__types'].keys()), { + 'wagtailcore.Page', + 'demosite.HomePage', 'demosite.BlogIndexPage', 'demosite.BlogEntryPageCarouselItem', 'demosite.BlogEntryPage', @@ -623,10 +625,23 @@ class TestAdminPageDetail(AdminAPITestCase, TestPageDetail): 'listing_url': 'http://localhost/admin/api/v2beta/pages/?descendant_of=2' }) + def test_meta_ancestors(self): + # Homepage should have children + response = self.get_response(16) + content = json.loads(response.content.decode('UTF-8')) + + self.assertIn('ancestors', content['meta']) + self.assertIsInstance(content['meta']['ancestors'], list) + self.assertEqual(len(content['meta']['ancestors']), 3) + self.assertEqual(content['meta']['ancestors'][0].keys(), {'id', 'meta', 'title', 'admin_display_title'}) + self.assertEqual(content['meta']['ancestors'][0]['title'], 'Root') + self.assertEqual(content['meta']['ancestors'][1]['title'], 'Home page') + self.assertEqual(content['meta']['ancestors'][2]['title'], 'Blog index') + # FIELDS def test_remove_all_meta_fields(self): - response = self.get_response(16, fields='-type,-detail_url,-slug,-first_published_at,-html_url,-descendants,-latest_revision_created_at,-children,-show_in_menus,-seo_title,-parent,-status,-search_description') + response = self.get_response(16, fields='-type,-detail_url,-slug,-first_published_at,-html_url,-descendants,-latest_revision_created_at,-children,-ancestors,-show_in_menus,-seo_title,-parent,-status,-search_description') content = json.loads(response.content.decode('UTF-8')) self.assertNotIn('meta', set(content.keys()))