Use HTMX for search page

This commit is contained in:
Jake Howard 2022-07-31 15:28:48 +01:00
parent 53123ad93e
commit fb78f3f993
Signed by: jake
GPG key ID: 57AFB45680EDD477
4 changed files with 63 additions and 52 deletions

View file

@ -40,6 +40,7 @@
<script async defer type="text/javascript" src="{% static 'js/base.js' %}"></script> <script async defer type="text/javascript" src="{% static 'js/base.js' %}"></script>
<script async defer type="text/javascript" src="{% static 'contrib/htmx/htmx.min.js' %}"></script>
{% block darkmode %} {% block darkmode %}
<script type="text/javascript" src="{% static 'js/darkreader.js' %}"></script> <script type="text/javascript" src="{% static 'js/darkreader.js' %}"></script>

View file

@ -1,7 +1,10 @@
from django.core.paginator import EmptyPage, Paginator from django.core.paginator import EmptyPage, Paginator
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.http.response import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render
from django.utils.functional import cached_property from django.utils.functional import cached_property
from rest_framework import serializers from rest_framework import serializers
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.models import Page from wagtail.models import Page
from wagtail.query import PageQuerySet from wagtail.query import PageQuerySet
from wagtail.search.models import Query from wagtail.search.models import Query
@ -12,7 +15,7 @@ from website.common.utils import TocEntry
from website.home.models import HomePage from website.home.models import HomePage
class SearchPage(BaseContentMixin, BasePage): # type: ignore[misc] class SearchPage(BaseContentMixin, RoutablePageMixin, BasePage): # type: ignore[misc]
max_count = 1 max_count = 1
subpage_types: list = [] subpage_types: list = []
parent_page_types = ["home.HomePage"] parent_page_types = ["home.HomePage"]
@ -40,20 +43,35 @@ class SearchPage(BaseContentMixin, BasePage): # type: ignore[misc]
def get_context(self, request: HttpRequest) -> dict: def get_context(self, request: HttpRequest) -> dict:
context = super().get_context(request) context = super().get_context(request)
context["search_url"] = self.reverse_subpage("results")
return context
@route(r"^results/$")
def results(self, request: HttpRequest) -> HttpResponse:
if not request.GET.get("q", None):
return HttpResponse()
serializer = self.SearchParamsSerializer(data=request.GET) serializer = self.SearchParamsSerializer(data=request.GET)
if serializer.is_valid(): if not serializer.is_valid():
return HttpResponseBadRequest(serializer.errors)
search_query = serializer.validated_data["q"] search_query = serializer.validated_data["q"]
context["search_query"] = search_query page_num = serializer.validated_data["page"]
context = {
**self.get_context(request),
"search_query": search_query,
"page_num": page_num,
}
filters, query = parse_query_string(search_query) filters, query = parse_query_string(search_query)
Query.get(search_query).add_hit() Query.get(search_query).add_hit()
pages = self.get_search_pages().search(query) pages = self.get_search_pages().search(query)
paginator = Paginator(pages, self.PAGE_SIZE) paginator = Paginator(pages, self.PAGE_SIZE)
context["paginator"] = paginator context["paginator"] = paginator
page_num = serializer.validated_data["page"]
context["page_num"] = page_num
try: try:
results = paginator.page(page_num) results = paginator.page(page_num)
@ -61,19 +79,13 @@ class SearchPage(BaseContentMixin, BasePage): # type: ignore[misc]
if not isinstance(results.object_list, PageQuerySet): if not isinstance(results.object_list, PageQuerySet):
results.object_list = Page.objects.filter( results.object_list = Page.objects.filter(
id__in=list( id__in=list(
results.object_list.get_queryset().values_list( results.object_list.get_queryset().values_list("id", flat=True)
"id", flat=True
)
) )
).specific() ).specific()
except EmptyPage: except EmptyPage:
results = [] results = []
context["results"] = results context["results"] = results
else:
if "q" in request.GET:
context["invalid_search"] = True
else:
context["initial"] = True
return context return render(request, "search/search_results.html", context)

View file

@ -11,28 +11,17 @@
{% endif %} {% endif %}
<section class="container"> <section class="container">
<form> <input
<input type="search" name="q" placeholder="Search" value="{{ search_query }}"/> type="search"
</form> name="q"
</section> placeholder="Search"
hx-get="{{ search_url }}"
<section class="container"> hx-trigger="keyup changed delay:300ms, search"
{% if initial %} hx-target="#search-results"
<p>Enter search terms</p> autocomplete="off"
{% elif invalid_search %} >
<p>Invalid search</p>
{% elif results|length == 0 %}
{% if page_num > paginator.num_pages %}
<p>There aren't {{ page_num }} page - only {{ paginator.num_pages }}.</p>
{% else %}
<p>No results</p>
{% endif %}
{% else %}
{% for page in results %}
{% include "common/listing-item.html" %}
{% endfor %}
{% endif %}
</section> </section>
<section class="container" id="search-results"></section>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,9 @@
{% load wagtailadmin_tags %}
{% for page in results %}
{% include "common/listing-item.html" %}
{% endfor %}
{% if results.has_next %}
<span hx-get="{{ search_url }}{% querystring page=results.next_page_number %}" hx-trigger="revealed" hx-swap="outerHTML"></span>
{% endif %}