Merge branch 'wagtailapi-descendant-of'

This commit is contained in:
Karl Hobley 2015-07-15 10:01:47 +01:00
commit f84af79c7d
6 changed files with 135 additions and 3 deletions

View file

@ -9,6 +9,7 @@ Changelog
* The `{% image %}` tag now supports filters on the image variable, e.g. `{% image primary_img|default:secondary_img width-500 %}`
* Moved the style guide menu item into the Settings sub-menu
* Search backends can now be specified by module (e.g. `wagtail.wagtailsearch.backends.elasticsearch`), rather than a specific class (`wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch`)
* Added ``descendant_of`` filter to the API (Michael Fillier)
* Fix: Text areas in the non-default tab of the page editor now resize to the correct height

View file

@ -54,6 +54,7 @@ Contributors
* Mac Chapman
* Brett Grace
* Nar Chhantyal
* Michael Fillier
Translators
===========

View file

@ -258,9 +258,14 @@ Exact matches on field values can be done by using a query parameter with the sa
Filtering by section of the tree
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is also possible to filter the listing to only include pages with a particular parent. This is useful if you have multiple blogs on your site and only want to view the contents of one of them.
It is also possible to filter the listing to only include pages with a particular parent or ancestor. This is useful if you have multiple blogs on your site and only want to view the contents of one of them.
For example (imagine we are in the same project as all previous examples, and page id ``7`` refers to the other blog index):
**child_of**
Filters the listing to only include direct children of the specified page.
For example, to get all the pages that are direct children of page 7.
.. code-block:: json
@ -286,6 +291,62 @@ For example (imagine we are in the same project as all previous examples, and pa
}
**descendant_of**
.. versionadded:: 1.1
Filters the listing to only include descendants of the specified page.
For example, to get all pages underneath the homepage:
.. code-block:: json
GET /api/v1/pages/?descendant_of=2
HTTP 200 OK
Content-Type: application/json
{
"meta": {
"total_count": 1
},
"pages": [
{
"id": 3,
"meta": {
"type": "demo.BlogIndexPage",
"detail_url": "http://api.example.com/api/v1/pages/3/"
},
"title": "Blog"
},
{
"id": 4,
"meta": {
"type": "demo.BlogPage",
"detail_url": "http://api.example.com/api/v1/pages/4/"
},
"title": "My blog 1",
},
{
"id": 5,
"meta": {
"type": "demo.BlogPage",
"detail_url": "http://api.example.com/api/v1/pages/5/"
},
"title": "My blog 2",
},
{
"id": 6,
"meta": {
"type": "demo.BlogPage",
"detail_url": "http://api.example.com/api/v1/pages/6/"
},
"title": "My blog 3",
}
]
}
Ordering
^^^^^^^^

View file

@ -22,3 +22,4 @@ Minor features
* The ``{% image %}`` tag now supports filters on the image variable, e.g. ``{% image primary_img|default:secondary_img width-500 %}``
* Moved the style guide menu item into the Settings sub-menu
* Search backends can now be specified by module (e.g. ``wagtail.wagtailsearch.backends.elasticsearch``), rather than a specific class (``wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch``)
* Added ``descendant_of`` filter to the API

View file

@ -316,6 +316,7 @@ class PagesAPIEndpoint(BaseAPIEndpoint):
known_query_parameters = BaseAPIEndpoint.known_query_parameters.union([
'type',
'child_of',
'descendant_of',
])
def get_queryset(self, request, model=Page):
@ -386,12 +387,32 @@ class PagesAPIEndpoint(BaseAPIEndpoint):
try:
parent_page = self.get_queryset(request).get(id=parent_page_id)
return queryset.child_of(parent_page)
queryset = queryset.child_of(parent_page)
queryset._filtered_by_child_of = True
return queryset
except Page.DoesNotExist:
raise BadRequestError("parent page doesn't exist")
return queryset
def do_descendant_of_filter(self, request, queryset):
if 'descendant_of' in request.GET:
if getattr(queryset, '_filtered_by_child_of', False):
raise BadRequestError("filtering by descendant_of with child_of is not supported")
try:
ancestor_page_id = int(request.GET['descendant_of'])
assert ancestor_page_id >= 0
except (ValueError, AssertionError):
raise BadRequestError("descendant_of must be a positive integer")
try:
ancestor_page = self.get_queryset(request).get(id=ancestor_page_id)
return queryset.descendant_of(ancestor_page)
except Page.DoesNotExist:
raise BadRequestError("ancestor page doesn't exist")
return queryset
def listing_view(self, request):
# Get model and queryset
model = self.get_model(request)
@ -403,6 +424,7 @@ class PagesAPIEndpoint(BaseAPIEndpoint):
# Filtering
queryset = self.do_field_filtering(request, queryset)
queryset = self.do_child_of_filter(request, queryset)
queryset = self.do_descendant_of_filter(request, queryset)
# Ordering
queryset = self.do_ordering(request, queryset)

View file

@ -292,6 +292,52 @@ class TestPageListing(TestCase):
self.assertEqual(content, {'message': "parent page doesn't exist"})
# DESCENDANT OF FILTER
def test_descendant_of_filter(self):
response = self.get_response(descendant_of=6)
content = json.loads(response.content.decode('UTF-8'))
page_id_list = self.get_page_id_list(content)
self.assertEqual(page_id_list, [10, 15, 17, 21, 22, 23])
def test_descendant_of_with_type(self):
response = self.get_response(type='tests.EventPage', descendant_of=6)
content = json.loads(response.content.decode('UTF-8'))
page_id_list = self.get_page_id_list(content)
self.assertEqual(page_id_list, [])
def test_descendant_of_unknown_page_gives_error(self):
response = self.get_response(descendant_of=1000)
content = json.loads(response.content.decode('UTF-8'))
self.assertEqual(response.status_code, 400)
self.assertEqual(content, {'message': "ancestor page doesn't exist"})
def test_descendant_of_not_integer_gives_error(self):
response = self.get_response(descendant_of='abc')
content = json.loads(response.content.decode('UTF-8'))
self.assertEqual(response.status_code, 400)
self.assertEqual(content, {'message': "descendant_of must be a positive integer"})
def test_descendant_of_page_thats_not_in_same_site_gives_error(self):
# Root page is not in any site, so pretend it doesn't exist
response = self.get_response(descendant_of=1)
content = json.loads(response.content.decode('UTF-8'))
self.assertEqual(response.status_code, 400)
self.assertEqual(content, {'message': "ancestor page doesn't exist"})
def test_descendant_of_when_filtering_by_child_of_gives_error(self):
response = self.get_response(descendant_of=6, child_of=5)
content = json.loads(response.content.decode('UTF-8'))
self.assertEqual(response.status_code, 400)
self.assertEqual(content, {'message': "filtering by descendant_of with child_of is not supported"})
# ORDERING
def test_ordering_default(self):