Update type(), not_type(), exact_type() and not_exact_type() to accept multiple model classes as arguments

pull/6849/head
Andy Babic 2021-02-27 09:50:28 +00:00 zatwierdzone przez Andy Babic
rodzic 49e18d7ad8
commit 8f38aaae87
3 zmienionych plików z 134 dodań i 41 usunięć
docs/reference/pages

Wyświetl plik

@ -207,9 +207,12 @@ Reference
.. code-block:: python
# Find all pages that are of type AbstractEmailForm, or a descendant of it
# Find all pages that are of type AbstractEmailForm, or one of it's subclasses
form_pages = Page.objects.type(AbstractEmailForm)
# Find all pages that are of type AbstractEmailForm or AbstractEventPage, or one of their subclasses
form_and_event_pages = Page.objects.type(AbstractEmailForm, AbstractEventPage)
.. automethod:: not_type
.. automethod:: exact_type
@ -221,14 +224,30 @@ Reference
# Find all pages that are of the exact type EventPage
event_pages = Page.objects.exact_type(EventPage)
# Find all page of the exact type EventPage or NewsPage
news_and_events_pages = Page.objects.exact_type(EventPage, NewsPage)
.. note::
If you are only interested in pages of a single type, it is clearer (and often more efficient) to use
the specific model's manager to get a queryset. For example:
.. code-block:: python
event_pages = EventPage.objects.all()
.. automethod:: not_exact_type
Example:
.. code-block:: python
# Find all pages that are not of the exact type EventPage (but may be a subclass)
non_event_pages = Page.objects.not_exact_type(EventPage)
# First, find all news and event pages
news_and_events = Page.objects.type(NewsPage, EventPage)
# Now exclude pages with an exact type of EventPage or NewsPage,
# leaving only instance of more 'specialist' types
specialised_news_and_events = news_and_events.not_exact_type(NewsPage, EventPage)
.. automethod:: unpublish

Wyświetl plik

@ -180,43 +180,44 @@ class PageQuerySet(SearchableQuerySetMixin, TreeQuerySet):
"""
return self.exclude(self.page_q(other))
def type_q(self, klass):
content_types = ContentType.objects.get_for_models(*[
def type_q(self, *types):
all_subclasses = set(
model for model in apps.get_models()
if issubclass(model, klass)
]).values()
if issubclass(model, types)
)
content_types = ContentType.objects.get_for_models(*all_subclasses)
return Q(content_type__in=list(content_types.values()))
return Q(content_type__in=list(content_types))
def type(self, model):
def type(self, *types):
"""
This filters the QuerySet to only contain pages that are an instance
of the specified model (including subclasses).
of the specified model(s) (including subclasses).
"""
return self.filter(self.type_q(model))
return self.filter(self.type_q(*types))
def not_type(self, model):
def not_type(self, *types):
"""
This filters the QuerySet to not contain any pages which are an instance of the specified model.
This filters the QuerySet to exclude any pages which are an instance of the specified model(s).
"""
return self.exclude(self.type_q(model))
return self.exclude(self.type_q(*types))
def exact_type_q(self, klass):
return Q(content_type=ContentType.objects.get_for_model(klass))
def exact_type_q(self, *types):
content_types = ContentType.objects.get_for_models(*types)
return Q(content_type__in=list(content_types.values()))
def exact_type(self, model):
def exact_type(self, *types):
"""
This filters the QuerySet to only contain pages that are an instance of the specified model
This filters the QuerySet to only contain pages that are an instance of the specified model(s)
(matching the model exactly, not subclasses).
"""
return self.filter(self.exact_type_q(model))
return self.filter(self.exact_type_q(*types))
def not_exact_type(self, model):
def not_exact_type(self, *types):
"""
This filters the QuerySet to not contain any pages which are an instance of the specified model
This filters the QuerySet to exclude any pages which are an instance of the specified model(s)
(matching the model exactly, not subclasses).
"""
return self.exclude(self.exact_type_q(model))
return self.exclude(self.exact_type_q(*types))
def public_q(self):
from wagtail.core.models import PageViewRestriction

Wyświetl plik

@ -298,24 +298,34 @@ class TestPageQuerySet(TestCase):
# Check that "someone elses event" is in the results
event = Page.objects.get(url_path='/home/events/someone-elses-event/')
self.assertTrue(pages.filter(id=event.id).exists())
self.assertIn(event, pages)
# Check that "Saint Patrick" (an instance of SingleEventPage, a subclass of EventPage)
# is in the results
event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertTrue(pages.filter(id=event.id).exists())
self.assertIn(event, pages)
def test_type_includes_subclasses(self):
from wagtail.contrib.forms.models import AbstractEmailForm
pages = Page.objects.type(AbstractEmailForm)
def test_type_with_multiple_models(self):
pages = Page.objects.type(EventPage, SimplePage)
# Check that all objects are instances of AbstractEmailForm
# Check that all objects are EventPages or SimplePages
for page in pages:
self.assertIsInstance(page.specific, AbstractEmailForm)
self.assertTrue(
isinstance(page.specific, (EventPage, SimplePage))
)
# Check that the contact form page is in the results
contact_us = Page.objects.get(url_path='/home/contact-us/')
self.assertTrue(pages.filter(id=contact_us.id).exists())
# Check that "someone elses event" is in the results
event = Page.objects.get(url_path='/home/events/someone-elses-event/')
self.assertIn(event, pages)
# Check that "Saint Patrick" (an instance of SingleEventPage, a subclass of EventPage)
# is in the results
event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertIn(event, pages)
# Check that "About us" (an instance of SimplePage) is in the results
about_us = Page.objects.get(url_path='/home/about-us/')
self.assertIn(about_us, pages)
def test_not_type(self):
pages = Page.objects.not_type(EventPage)
@ -324,41 +334,104 @@ class TestPageQuerySet(TestCase):
for page in pages:
self.assertNotIsInstance(page.specific, EventPage)
# Check that "About us" is in the results
about_us = Page.objects.get(url_path='/home/about-us/')
self.assertIn(about_us, pages)
# Check that the homepage is in the results
homepage = Page.objects.get(url_path='/home/')
self.assertTrue(pages.filter(id=homepage.id).exists())
self.assertIn(homepage, pages)
def test_not_type_with_multiple_models(self):
pages = Page.objects.not_type(EventPage, SimplePage)
# Check that no objects are EventPages or SimplePages
for page in pages:
self.assertFalse(
isinstance(page.specific, (EventPage, SimplePage))
)
# Check that "About us" is NOT in the results
about_us = Page.objects.get(url_path='/home/about-us/')
self.assertNotIn(about_us, pages)
# Check that the homepage IS in the results
homepage = Page.objects.get(url_path='/home/')
self.assertIn(homepage, pages)
def test_exact_type(self):
pages = Page.objects.exact_type(EventPage)
# Check that all objects are EventPages (and not a subclass)
for page in pages:
self.assertEqual(type(page.specific), EventPage)
self.assertIs(page.specific_class, EventPage)
# Check that "someone elses event" is in the results
event = Page.objects.get(url_path='/home/events/someone-elses-event/')
self.assertTrue(pages.filter(id=event.id).exists())
self.assertIn(event, pages)
# Check that "Saint Patrick" (an instance of SingleEventPage, a subclass of EventPage)
# is NOT in the results
event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertFalse(pages.filter(id=event.id).exists())
single_event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertNotIn(single_event, pages)
def test_exact_type_with_multiple_models(self):
pages = Page.objects.exact_type(EventPage, Page)
# Check that all objects are EventPages or Pages (and not a subclass)
for page in pages:
self.assertIn(page.specific_class, (EventPage, Page))
# Check that "someone elses event" is in the results
event = Page.objects.get(url_path='/home/events/someone-elses-event/')
self.assertIn(event, pages)
# Check that "Saint Patrick" (an instance of SingleEventPage, a subclass of EventPage
# and Page) is NOT in the results
single_event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertNotIn(single_event, pages)
# Check that the homepage (a generic Page only) is in the results
homepage = Page.objects.get(url_path='/home/')
self.assertIn(homepage, pages)
# Check that "About us" (an instance of SimplePage, a subclass of Page)
# is NOT in the results
about_us = Page.objects.get(url_path='/home/about-us/')
self.assertNotIn(about_us, pages)
def test_not_exact_type(self):
pages = Page.objects.not_exact_type(EventPage)
# Check that no objects are EventPages
for page in pages:
self.assertNotEqual(type(page.specific), EventPage)
self.assertIsNot(page.specific_class, EventPage)
# Check that the homepage is in the results
homepage = Page.objects.get(url_path='/home/')
self.assertTrue(pages.filter(id=homepage.id).exists())
self.assertIn(homepage, pages)
# Check that "Saint Patrick" (an instance of SingleEventPage, a subclass of EventPage)
# is in the results
event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertTrue(pages.filter(id=event.id).exists())
self.assertIn(event, pages)
def test_not_exact_type_with_multiple_models(self):
pages = Page.objects.not_exact_type(EventPage, Page)
# Check that no objects are EventPages or generic Pages
for page in pages:
self.assertNotIn(page.specific_class, (EventPage, Page))
# Check that "Saint Patrick" (an instance of SingleEventPage, a subclass of EventPage)
# is in the results
event = Page.objects.get(url_path='/home/events/saint-patrick/')
self.assertIn(event, pages)
# Check that "About us" (an instance of SimplePage, a subclass of Page)
# is in the results
about_us = Page.objects.get(url_path='/home/about-us/')
self.assertIn(about_us, pages)
def test_public(self):
events_index = Page.objects.get(url_path='/home/events/')