Fix Page queryset.not_public returning all pages when no page restrictions exist. (#9067)

Fixes #8952
pull/9076/head
Mehrdad Moradizadeh 2022-08-22 16:06:03 -04:00 zatwierdzone przez Matt Westcott
rodzic 86c3dbb961
commit 180d43a200
5 zmienionych plików z 56 dodań i 7 usunięć

Wyświetl plik

@ -6,6 +6,8 @@ Changelog
~~~~~~~~~~~~~~~~~~~~
* Add basic keyboard control and screen reader support for page listing re-ordering (Paarth Agarwal, Thomas van der Hoeven)
* Add `PageQuerySet.private` method as an alias of `not_public` (Mehrdad Moradizadeh)
* Fix: Prevent `PageQuerySet.not_public` from returning all pages when no page restrictions exist (Mehrdad Moradizadeh)
4.0 (xx.xx.xxxx) - IN DEVELOPMENT

Wyświetl plik

@ -185,6 +185,12 @@ menu_items = homepage.get_children().live().in_menu()
.. automethod:: not_public
.. automethod:: private
.. versionadded:: 4.1
The ``private`` method was added as an alias of ``not_public``.
.. automethod:: search
See: :ref:`wagtailsearch_searching_pages`

Wyświetl plik

@ -14,9 +14,12 @@ Wagtail 4.1 is designated a Long Term Support (LTS) release. Long Term Support r
### Other features
* Add basic keyboard control and screen reader support for page listing re-ordering (Paarth Agarwal, Thomas van der Hoeven)
* Add `PageQuerySet.private` method as an alias of `not_public` (Mehrdad Moradizadeh)
### Bug fixes
* Prevent `PageQuerySet.not_public` from returning all pages when no page restrictions exist (Mehrdad Moradizadeh)
## Upgrade considerations

Wyświetl plik

@ -235,25 +235,36 @@ class PageQuerySet(SearchableQuerySetMixin, TreeQuerySet):
"""
return self.exclude(self.exact_type_q(*types))
def public_q(self):
def private_q(self):
from wagtail.models import PageViewRestriction
q = Q()
for restriction in PageViewRestriction.objects.select_related("page").all():
q &= ~self.descendant_of_q(restriction.page, inclusive=True)
return q
q |= self.descendant_of_q(restriction.page, inclusive=True)
# do not match any page if no private section exists.
return q if q else Q(pk__in=[])
def public(self):
"""
This filters the QuerySet to only contain pages that are not in a private section
Filters the QuerySet to only contain pages that are not in a private
section and their descendants.
"""
return self.filter(self.public_q())
return self.exclude(self.private_q())
def not_public(self):
"""
This filters the QuerySet to only contain pages that are in a private section
Filters the QuerySet to only contain pages that are in a private
section and their descendants.
"""
return self.exclude(self.public_q())
return self.filter(self.private_q())
def private(self):
"""
Filters the QuerySet to only contain pages that are in a private
section and their descendants.
"""
return self.filter(self.private_q())
def first_common_ancestor(self, include_self=False, strict=False):
"""

Wyświetl plik

@ -480,6 +480,33 @@ class TestPageQuerySet(TestCase):
# Check that the event is in the results
self.assertTrue(pages.filter(id=event.id).exists())
def test_private(self):
events_index = Page.objects.get(url_path="/home/events/")
event = Page.objects.get(url_path="/home/events/christmas/")
homepage = Page.objects.get(url_path="/home/")
# Add PageViewRestriction to events_index
PageViewRestriction.objects.create(page=events_index, password="hello")
with self.assertNumQueries(4):
# Get public pages
pages = Page.objects.private()
# Check that the homepage is not in the results
self.assertFalse(pages.filter(id=homepage.id).exists())
# Check that the events index is in the results
self.assertTrue(pages.filter(id=events_index.id).exists())
# Check that the event is in the results
self.assertTrue(pages.filter(id=event.id).exists())
def test_private_with_no_private_page(self):
PageViewRestriction.objects.all().delete()
count = Page.objects.private().count()
self.assertEqual(count, 0)
def test_merge_queries(self):
type_q = Page.objects.type_q(EventPage)
query = Q()