mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-01 03:54:48 +00:00
Merge branch 'wagtailapi-descendant-of'
This commit is contained in:
commit
f84af79c7d
6 changed files with 135 additions and 3 deletions
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ Contributors
|
|||
* Mac Chapman
|
||||
* Brett Grace
|
||||
* Nar Chhantyal
|
||||
* Michael Fillier
|
||||
|
||||
Translators
|
||||
===========
|
||||
|
|
|
|||
|
|
@ -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
|
||||
^^^^^^^^
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in a new issue