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.
This commit is contained in:
parent
a98d36da14
commit
653847df6a
@ -7,6 +7,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|||||||
from modelcluster.contrib.taggit import ClusterTaggableManager
|
from modelcluster.contrib.taggit import ClusterTaggableManager
|
||||||
from modelcluster.fields import ParentalKey
|
from modelcluster.fields import ParentalKey
|
||||||
from taggit.models import TaggedItemBase
|
from taggit.models import TaggedItemBase
|
||||||
|
from wagtail.search import index
|
||||||
|
|
||||||
def _get_env_int(name, default):
|
def _get_env_int(name, default):
|
||||||
value = os.environ.get(name)
|
value = os.environ.get(name)
|
||||||
@ -272,6 +273,12 @@ class ArticlePage(Page):
|
|||||||
trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示")
|
trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示")
|
||||||
tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True)
|
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 + [
|
content_panels = Page.content_panels + [
|
||||||
FieldPanel("trending"),
|
FieldPanel("trending"),
|
||||||
FieldPanel("cover_image"),
|
FieldPanel("cover_image"),
|
||||||
@ -299,3 +306,25 @@ class ArticlePage(Page):
|
|||||||
|
|
||||||
context["related_articles"] = related_articles
|
context["related_articles"] = related_articles
|
||||||
return context
|
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))
|
||||||
|
|||||||
@ -38,6 +38,7 @@ def hashtag_search(request, slug):
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"site_root": site_root,
|
"site_root": site_root,
|
||||||
|
"page": site_root.specific if site_root else None,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "home/hashtag_page.html", context)
|
return render(request, "home/hashtag_page.html", context)
|
||||||
|
|||||||
@ -56,5 +56,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<form class="header-search" action="{% url 'search' %}" method="get" role="search">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
name="query"
|
||||||
|
placeholder="搜尋文章"
|
||||||
|
value="{{ request.GET.query|default:'' }}"
|
||||||
|
aria-label="搜尋文章">
|
||||||
|
<button type="submit">搜尋</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -1,38 +1,38 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static wagtailcore_tags %}
|
{% load wagtailcore_tags %}
|
||||||
|
|
||||||
{% block body_class %}template-searchresults{% endblock %}
|
{% block body_class %}template-searchresults{% endblock %}
|
||||||
|
|
||||||
{% block title %}Search{% endblock %}
|
{% block title %}
|
||||||
|
{% if search_query %}搜尋:{{ search_query }}{% else %}搜尋{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Search</h1>
|
<nav class="breadcrumbs" aria-label="breadcrumb">
|
||||||
|
<ol>
|
||||||
<form action="{% url 'search' %}" method="get">
|
|
||||||
<input type="text" name="query"{% if search_query %} value="{{ search_query }}"{% endif %}>
|
|
||||||
<input type="submit" value="Search" class="button">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% if search_results %}
|
|
||||||
<ul>
|
|
||||||
{% for result in search_results %}
|
|
||||||
<li>
|
<li>
|
||||||
<h4><a href="{% pageurl result %}">{{ result }}</a></h4>
|
{% if site_root %}
|
||||||
{% if result.search_description %}
|
<a href="{{ site_root.url }}">首頁</a>
|
||||||
{{ result.search_description }}
|
{% else %}
|
||||||
|
<a href="/">首頁</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
<li><span>搜尋</span></li>
|
||||||
</ul>
|
{% if search_query %}
|
||||||
|
<li><span>{{ search_query }}</span></li>
|
||||||
|
{% endif %}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
{% if search_results.has_previous %}
|
<section class="search-results">
|
||||||
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&page={{ search_results.previous_page_number }}">Previous</a>
|
{% if search_query %}
|
||||||
{% endif %}
|
{% if results_count %}
|
||||||
|
{% include "home/includes/page-article-list.html" %}
|
||||||
{% if search_results.has_next %}
|
{% else %}
|
||||||
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&page={{ search_results.next_page_number }}">Next</a>
|
<p>找不到與「{{ search_query }}」相關的文章。</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif search_query %}
|
{% else %}
|
||||||
No results found
|
<p>請輸入關鍵字後再進行搜尋。</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -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 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
|
from home.models import ArticlePage, PAGE_SIZE
|
||||||
# <https://docs.wagtail.org/en/stable/reference/contrib/searchpromotions.html>
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
def search(request):
|
def search(request):
|
||||||
search_query = request.GET.get("query", None)
|
search_query = (request.GET.get("query") or "").strip()
|
||||||
page = request.GET.get("page", 1)
|
page_number = request.GET.get("page", 1)
|
||||||
|
category_sections = []
|
||||||
|
results_page = None
|
||||||
|
results_count = 0
|
||||||
|
|
||||||
# Search
|
|
||||||
if search_query:
|
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)
|
site = Site.find_for_request(request)
|
||||||
# query.add_hit()
|
site_root = site.root_page if site else None
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"search/search.html",
|
"search/search.html",
|
||||||
{
|
{
|
||||||
"search_query": search_query,
|
"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,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user