diff --git a/website/blog/models.py b/website/blog/models.py index 921e697..6eafd57 100644 --- a/website/blog/models.py +++ b/website/blog/models.py @@ -8,7 +8,7 @@ from modelcluster.fields import ParentalManyToManyField from wagtail.admin.panels import FieldPanel from website.common.models import BaseContentPage, BaseListingPage -from website.common.utils import TocEntry +from website.common.utils import TocEntry, prefetch_for_listing from website.common.views import ContentPageFeed @@ -37,12 +37,8 @@ class BlogPostListPage(BaseListingPage): return [TocEntry(post_month, post_month, 0, []) for post_month in post_months] def get_listing_pages(self) -> models.QuerySet: - return ( - BlogPostPage.objects.descendant_of(self) - .live() - .select_related("hero_image", "hero_unsplash_photo") - .prefetch_related("tags") - .order_by("-date", "title") + return prefetch_for_listing( + BlogPostPage.objects.descendant_of(self).live().order_by("-date", "title").prefetch_related("tags") ) @property @@ -117,9 +113,8 @@ class BlogPostCollectionPage(BaseListingPage): subpage_types = [BlogPostPage] def get_listing_pages(self) -> models.QuerySet: - return ( - BlogPostPage.objects.child_of(self) - .select_related("hero_image", "hero_unsplash_photo") + return prefetch_for_listing( + BlogPostPage.objects.child_of(self).live() .prefetch_related("tags") .order_by("-date", "title") ) diff --git a/website/common/models.py b/website/common/models.py index f645e9a..538613c 100644 --- a/website/common/models.py +++ b/website/common/models.py @@ -22,12 +22,18 @@ from wagtail.search import index from wagtail.snippets.models import register_snippet from wagtailmetadata.models import MetadataMixin -from website.common.utils import count_words from website.contrib.unsplash.widgets import UnsplashPhotoChooser from .serializers import PaginationSerializer from .streamfield import add_heading_anchors, get_blocks, get_content_html -from .utils import TocEntry, extract_text, get_table_of_contents, truncate_string +from .utils import ( + TocEntry, + count_words, + extract_text, + get_table_of_contents, + prefetch_for_listing, + truncate_string, +) class BasePage(Page): @@ -196,12 +202,8 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage): abstract = True def get_listing_pages(self) -> models.QuerySet: - return ( - self.get_children() - .live() - .specific() - .select_related("hero_image", "hero_unsplash_photo") - .order_by("title") + return prefetch_for_listing( + self.get_children().live().specific().order_by("title") ) def get_paginator_page(self) -> PaginatorPage: diff --git a/website/common/utils.py b/website/common/utils.py index 54fbcd1..e8b063e 100644 --- a/website/common/utils.py +++ b/website/common/utils.py @@ -9,6 +9,7 @@ from django.utils.text import slugify, smart_split from more_itertools import ilen from wagtail.models import Page from wagtail.models import get_page_models as get_wagtail_page_models +from wagtail.query import PageQuerySet HEADER_TAGS = ["h2", "h3", "h4", "h5", "h6"] @@ -84,3 +85,13 @@ def extract_text(html: str) -> str: def truncate_string(text: str, words: int) -> str: return " ".join(islice(smart_split(text), words)) + + +def prefetch_for_listing(queryset: PageQuerySet) -> PageQuerySet: + """ + Prefetch a queryset ready for listing. + + This should be a queryset method, but dealing with lots of + different page models is a pain. + """ + return queryset.select_related("hero_image", "hero_unsplash_photo") diff --git a/website/search/models.py b/website/search/models.py index 8b7589f..6544207 100644 --- a/website/search/models.py +++ b/website/search/models.py @@ -10,7 +10,7 @@ from wagtail.models import Page from wagtail.search.utils import parse_query_string from website.common.models import BaseContentPage -from website.common.utils import TocEntry +from website.common.utils import TocEntry, prefetch_for_listing from website.home.models import HomePage from .serializers import MIN_SEARCH_LENGTH, SearchParamsSerializer @@ -75,10 +75,8 @@ class SearchPage(RoutablePageMixin, BaseContentPage): results = paginator.page(page_num) # HACK: Search results aren't a queryset, so we can't call `.specific` on it. This forces it to one as efficiently as possible - results.object_list = ( - results.object_list.get_queryset() - .specific() - .select_related("hero_image", "hero_unsplash_photo") + results.object_list = prefetch_for_listing( + results.object_list.get_queryset().specific() ) except EmptyPage: