From 653847df6a55fc03a0108d00bb930ebf81db9982 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Mon, 10 Nov 2025 16:42:15 +0900 Subject: [PATCH] Add search functionality to ArticlePage and enhance search templates - Implement search fields in ArticlePage model for indexing. - Update hashtag search view to include site root in context. - Enhance header with a search form for articles. - Modify search results template to improve user experience and display. --- innovedus_cms/home/models.py | 29 ++++++++++ innovedus_cms/home/views.py | 1 + .../mysite/templates/includes/header.html | 10 ++++ .../search/templates/search/search.html | 58 +++++++++---------- innovedus_cms/search/views.py | 58 ++++++++++--------- 5 files changed, 99 insertions(+), 57 deletions(-) diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index e833e0b..f062e57 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -7,6 +7,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.fields import ParentalKey from taggit.models import TaggedItemBase +from wagtail.search import index def _get_env_int(name, default): value = os.environ.get(name) @@ -272,6 +273,12 @@ class ArticlePage(Page): trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示") tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True) + search_fields = Page.search_fields + [ + index.SearchField("intro", partial_match=True), + index.SearchField("body_search_text", partial_match=True), + index.SearchField("tag_names_search_text", partial_match=True), + ] + content_panels = Page.content_panels + [ FieldPanel("trending"), FieldPanel("cover_image"), @@ -299,3 +306,25 @@ class ArticlePage(Page): context["related_articles"] = related_articles return context + + @property + def body_search_text(self): + if not self.body: + return "" + + excluded_types = {"image", "embed", "hr", "html"} + chunks = [] + + for block in self.body: + if block.block_type in excluded_types: + continue + # Each block decides how to expose searchable text + block_content = block.block.get_searchable_content(block.value) + if block_content: + chunks.extend(block_content) + + return " ".join(text for text in chunks if isinstance(text, str)) + + @property + def tag_names_search_text(self): + return " ".join(self.tags.values_list("name", flat=True)) diff --git a/innovedus_cms/home/views.py b/innovedus_cms/home/views.py index a64bfce..1dbfe37 100644 --- a/innovedus_cms/home/views.py +++ b/innovedus_cms/home/views.py @@ -38,6 +38,7 @@ def hashtag_search(request, slug): } ], "site_root": site_root, + "page": site_root.specific if site_root else None, } return render(request, "home/hashtag_page.html", context) diff --git a/innovedus_cms/mysite/templates/includes/header.html b/innovedus_cms/mysite/templates/includes/header.html index c07be12..c2f028d 100644 --- a/innovedus_cms/mysite/templates/includes/header.html +++ b/innovedus_cms/mysite/templates/includes/header.html @@ -56,5 +56,15 @@ {% endif %} + + diff --git a/innovedus_cms/search/templates/search/search.html b/innovedus_cms/search/templates/search/search.html index 476427f..6d06863 100644 --- a/innovedus_cms/search/templates/search/search.html +++ b/innovedus_cms/search/templates/search/search.html @@ -1,38 +1,38 @@ {% extends "base.html" %} -{% load static wagtailcore_tags %} +{% load wagtailcore_tags %} {% block body_class %}template-searchresults{% endblock %} -{% block title %}Search{% endblock %} +{% block title %} + {% if search_query %}搜尋:{{ search_query }}{% else %}搜尋{% endif %} +{% endblock %} {% block content %} -

Search

- -
- - -
- -{% if search_results %} - + +
  • 搜尋
  • + {% if search_query %} +
  • {{ search_query }}
  • + {% endif %} + + -{% if search_results.has_previous %} -Previous -{% endif %} - -{% if search_results.has_next %} -Next -{% endif %} -{% elif search_query %} -No results found -{% endif %} +
    + {% if search_query %} + {% if results_count %} + {% include "home/includes/page-article-list.html" %} + {% else %} +

    找不到與「{{ search_query }}」相關的文章。

    + {% endif %} + {% else %} +

    請輸入關鍵字後再進行搜尋。

    + {% endif %} +
    {% endblock %} diff --git a/innovedus_cms/search/views.py b/innovedus_cms/search/views.py index 678bb7e..e76fbf1 100644 --- a/innovedus_cms/search/views.py +++ b/innovedus_cms/search/views.py @@ -1,46 +1,48 @@ -from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from urllib.parse import urlencode + +from django.core.paginator import Paginator from django.template.response import TemplateResponse -from wagtail.models import Page +from wagtail.models import Site -# To enable logging of search queries for use with the "Promoted search results" module -# -# uncomment the following line and the lines indicated in the search function -# (after adding wagtail.contrib.search_promotions to INSTALLED_APPS): - -# from wagtail.contrib.search_promotions.models import Query +from home.models import ArticlePage, PAGE_SIZE def search(request): - search_query = request.GET.get("query", None) - page = request.GET.get("page", 1) + search_query = (request.GET.get("query") or "").strip() + page_number = request.GET.get("page", 1) + category_sections = [] + results_page = None + results_count = 0 - # Search if search_query: - search_results = Page.objects.live().search(search_query) + search_queryset = ArticlePage.objects.live().search(search_query) + paginator = Paginator(search_queryset, PAGE_SIZE) + results_page = paginator.get_page(page_number) + results_count = paginator.count - # To log this query for use with the "Promoted search results" module: + if results_count: + query_string = urlencode({"query": search_query}) + category_sections = [ + { + "title": f"搜尋:{search_query}", + "items": results_page, + "url": f"{request.path}?{query_string}", + } + ] - # query = Query.get(search_query) - # query.add_hit() - - else: - search_results = Page.objects.none() - - # Pagination - paginator = Paginator(search_results, 10) - try: - search_results = paginator.page(page) - except PageNotAnInteger: - search_results = paginator.page(1) - except EmptyPage: - search_results = paginator.page(paginator.num_pages) + site = Site.find_for_request(request) + site_root = site.root_page if site else None return TemplateResponse( request, "search/search.html", { "search_query": search_query, - "search_results": search_results, + "category_sections": category_sections, + "results_page": results_page, + "results_count": results_count, + "site_root": site_root, + "page": site_root.specific if site_root else None, }, )