Browse Source

Search all Wagtail Page models (#501)

Jeremy Childers 2 years ago
parent
commit
2cc984bfd0

+ 0 - 12
coderedcms/models/page_models.py

@@ -107,18 +107,6 @@ def get_page_models():
 class CoderedPageMeta(PageBase):
 class CoderedPageMeta(PageBase):
     def __init__(cls, name, bases, dct):
     def __init__(cls, name, bases, dct):
         super().__init__(name, bases, dct)
         super().__init__(name, bases, dct)
-        if 'search_db_include' not in dct:
-            cls.search_db_include = False
-        if 'search_db_boost' not in dct:
-            cls.search_db_boost = 0
-        if 'search_filterable' not in dct:
-            cls.search_filterable = False
-        if 'search_name' not in dct:
-            cls.search_name = cls._meta.verbose_name
-        if 'search_name_plural' not in dct:
-            cls.search_name_plural = cls._meta.verbose_name_plural
-        if 'search_template' not in dct:
-            cls.search_template = 'coderedcms/pages/search_result.html'
         if not cls._meta.abstract:
         if not cls._meta.abstract:
             CODERED_PAGE_MODELS.append(cls)
             CODERED_PAGE_MODELS.append(cls)
 
 

+ 77 - 63
coderedcms/templates/coderedcms/pages/search.html

@@ -1,74 +1,88 @@
-{% extends "coderedcms/pages/web_page.html" %}
+{% extends "coderedcms/pages/web_page_notitle.html" %}
-{% load bootstrap4 i18n coderedcms_tags %}
+{% load bootstrap4 i18n coderedcms_tags wagtailcore_tags %}
 
 
-{% block title %}
+{% wagtail_site as site %}
-    {% if not form.s.value %}
+
-        {% trans 'Search' %}
+{% block extra_head %}
-    {% else %}
+<title>{% if not form.s.value %}{% trans 'Search' %}{% else %}{% trans 'Search for' %} “{{form.s.value}}”{% endif %} — {{ site.site_name }}</title>
-        {% trans 'Search for' %} “{{form.s.value}}”
+{{ block.super }}
-    {%endif%}
 {% endblock %}
 {% endblock %}
 
 
-{% block content %}
+{% block content_pre_body %}
-    <div class="container">
+<div class="container my-5">
+  {% if not form.s.value %}
+  <h1>{% trans 'Search' %}</h1>
+  {% else %}
+  <h1>{% trans 'Search for' %} “{{form.s.value}}”</h1>
+  {% endif %}
+</div>
+{% endblock %}
 
 
-        <div class="mt-5">
+{% block content_body %}
-          {% if not form.s.value %}
+<div class="container">
-              <h2>{% trans 'Search' %}</h2>
+  {% block search_form %}
-          {% else %}
+  {% if not settings.coderedcms.LayoutSettings.navbar_search %}
-              <h2>{% trans 'Search for' %} “{{form.s.value}}”</h2>
+  <form class="mt-5" action="{% url 'codered_search' %}" method="GET">
-          {%endif%}
+    <div class="row">
+      <div class="col-sm-9">
+        {% bootstrap_form form size='large' layout='inline' %}
+      </div>
+      <div class="col-sm-3">
+        <div class="form-group">
+          <button class="btn btn-lg btn-block btn-outline-primary" type="submit">{% trans 'Search' %}</button>
         </div>
         </div>
+      </div>
+    </div>
+  </form>
+  {% endif %}
+  {% endblock search_form %}
 
 
-        {% if not settings.coderedcms.LayoutSettings.navbar_search %}
+  {% block search_page_types %}
-        <form class="mt-5" action="{% url 'codered_search' %}" method="GET">
+  {% if pagetypes %}
-            <div class="row">
+  {% query_update request.GET 'p' None as qs_nop %}
-                <div class="col-sm-9">
+  <div class="mt-5">
-                    {% bootstrap_form form size='large' layout='inline' %}
+    <ul class="nav nav-pills">
-                </div>
+      <li class="nav-item">
-                <div class="col-sm-3">
+        {% query_update qs_nop 't' None as qs_t %}
-                    <div class="form-group">
+        <a class="nav-link {% if not form.t.value %}active{% endif %}" href="?{{qs_t.urlencode}}">{% trans 'All Results' %}</a>
-                        <button class="btn btn-lg btn-block btn-outline-primary" type="submit">{% trans 'Search' %}</button>
+      </li>
-                    </div>
+      {% for pt in pagetypes %}
-                </div>
+      <li class="nav-item">
-            </div>
+        {% query_update qs_nop 't' pt.content_type.model as qs_t %}
-        </form>
+        <a class="nav-link {% if form.t.value == pt.content_type.model %}active{% endif %}" href="?{{qs_t.urlencode}}">{{pt|get_plural_name_of_class}}</a>
-        {% endif %}
+      </li>
+      {% endfor %}
+    </ul>
+  </div>
+  {% endif %}
+  {% endblock search_page_types %}
 
 
-        {% if pagetypes %}
+  <hr class="mb-5">
-        {% query_update request.GET 'p' None as qs_nop %}
-        <div class="mt-5">
-            <ul class="nav nav-pills">
-                <li class="nav-item">
-                    {% query_update qs_nop 't' None as qs_t %}
-                    <a class="nav-link {% if not form.t.value %}active{% endif %}" href="?{{qs_t.urlencode}}">{% trans 'All Results' %}</a>
-                </li>
-                {% for pt in pagetypes %}
-                <li class="nav-item">
-                    {% query_update qs_nop 't' pt.content_type.model as qs_t %}
-                    <a class="nav-link {% if form.t.value == pt.content_type.model %}active{% endif %}" href="?{{qs_t.urlencode}}">{{pt.search_name_plural}}</a>
-                </li>
-                {% endfor %}
-            </ul>
-        </div>
-        {% endif %}
 
 
-        <hr class="mb-5">
+  {% if results_paginated.object_list %}
 
 
+  {% block search_results %}
+  {% for page in results_paginated %}
+  <div class="mb-5">
+    {% with page=page.specific %}
+    {% if page.search_template %}
+    {% include page.search_template %}
+    {% else %}
+    {% include 'coderedcms/pages/search_result.html' %}
+    {% endif %}
+    {% endwith %}
+  </div>
+  {% endfor %}
+  {% include "coderedcms/includes/pagination.html" with items=results_paginated %}
+  {% endblock search_results %}
 
 
-        {% if results_paginated.object_list %}
+  {% elif form.s.value %}
-            {% for page in results_paginated %}
+
-            <div class="mb-5">
+  {% block search_noresults %}
-                {% with page=page.specific %}
+  <p>{% trans 'No results found.' %}</p>
-                    {% include page.search_template %}
+  {% endblock search_noresults %}
-                {% endwith %}
+
-            </div>
+  {% endif %}
-            {% endfor %}
+
-            {% include "coderedcms/includes/pagination.html" with items=results_paginated %}
+</div>
-        {% else %}
-            {% if form.s.value %}
-              <p>{% trans 'No results found.' %}</p>
-            {% endif %}
-        {% endif %}
-    </div>
 {% endblock %}
 {% endblock %}

+ 4 - 2
coderedcms/templates/coderedcms/pages/search_result.html

@@ -1,9 +1,11 @@
+{% load coderedcms_tags %}
+
 <div class="d-flex align-items-center">
 <div class="d-flex align-items-center">
     <h4><a href="{{page.url}}">{{page.title}}</a></h4>
     <h4><a href="{{page.url}}">{{page.title}}</a></h4>
-    {% if page.content_type.model_class in pagetypes %}<small class="ml-3 badge badge-secondary">{{page.search_name}}</small>{% endif %}
+    {% if page.content_type.model_class in pagetypes %}<small class="ml-3 badge badge-secondary">{{page|get_name_of_class}}</small>{% endif %}
 </div>
 </div>
 {% if page.search_description %}
 {% if page.search_description %}
     <p>{{page.search_description|safe}}</p>
     <p>{{page.search_description|safe}}</p>
 {% elif page.body_preview %}
 {% elif page.body_preview %}
     <p>{{page.body_preview}}</p>
     <p>{{page.body_preview}}</p>
-{% endif %}
+{% endif %}

+ 26 - 0
coderedcms/templatetags/coderedcms_tags.py

@@ -184,3 +184,29 @@ def map_to_bootstrap_alert(message_tag):
         return message_to_alert_dict[message_tag]
         return message_to_alert_dict[message_tag]
     except KeyError:
     except KeyError:
         return ''
         return ''
+
+
+@register.filter
+def get_name_of_class(class_type):
+    if hasattr(class_type.__class__, "search_name"):
+        return class_type.__class__.search_name
+    elif (
+            hasattr(class_type.__class__, "_meta") and
+            hasattr(class_type.__class__._meta, "verbose_name")
+    ):
+        return class_type.__class__._meta.verbose_name
+    else:
+        return class_type.__class__.__name__
+
+
+@register.filter
+def get_plural_name_of_class(class_type):
+    if hasattr(class_type.__class__, "search_name_plural"):
+        return class_type.__class__.search_name_plural
+    elif (
+            hasattr(class_type.__class__, "_meta") and
+            hasattr(class_type.__class__._meta, "verbose_name_plural")
+    ):
+        return class_type.__class__._meta.verbose_name_plural
+    else:
+        return class_type.__class__.__name__ + "s"

+ 18 - 46
coderedcms/views.py

@@ -1,6 +1,5 @@
 import mimetypes
 import mimetypes
 import os
 import os
-from itertools import chain
 from datetime import datetime
 from datetime import datetime
 from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect, JsonResponse
 from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect, JsonResponse
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.decorators import login_required
@@ -11,19 +10,18 @@ from django.utils import timezone
 from django.utils.translation import ngettext, gettext_lazy as _
 from django.utils.translation import ngettext, gettext_lazy as _
 from icalendar import Calendar
 from icalendar import Calendar
 from wagtail.admin import messages
 from wagtail.admin import messages
-from wagtail.search.backends import db, get_search_backend
+from wagtail.core.models import Page, get_page_models
-from wagtail.search.models import Query
 from coderedcms import utils
 from coderedcms import utils
 from coderedcms.forms import SearchForm
 from coderedcms.forms import SearchForm
 from coderedcms.models import (
 from coderedcms.models import (
     CoderedPage,
     CoderedPage,
     CoderedEventPage,
     CoderedEventPage,
-    get_page_models,
     GeneralSettings,
     GeneralSettings,
     LayoutSettings
     LayoutSettings
 )
 )
 from coderedcms.importexport import convert_csv_to_json, import_pages, ImportPagesFromCSVFileForm
 from coderedcms.importexport import convert_csv_to_json, import_pages, ImportPagesFromCSVFileForm
 from coderedcms.settings import crx_settings
 from coderedcms.settings import crx_settings
+from coderedcms.templatetags.coderedcms_tags import get_name_of_class
 
 
 
 
 def search(request):
 def search(request):
@@ -39,49 +37,26 @@ def search(request):
         search_query = search_form.cleaned_data['s']
         search_query = search_form.cleaned_data['s']
         search_model = search_form.cleaned_data['t']
         search_model = search_form.cleaned_data['t']
 
 
-        # get all codered models
+        # get all page models
-        pagemodels = sorted(get_page_models(), key=lambda k: k.search_name)
+        pagemodels = sorted(get_page_models(), key=get_name_of_class)
-        # get filterable models
+        # filter based on is search_filterable
         for model in pagemodels:
         for model in pagemodels:
-            if model.search_filterable:
+            if hasattr(model, "search_filterable") and model.search_filterable:
                 pagetypes.append(model)
                 pagetypes.append(model)
 
 
-        # get backend
+        results = Page.objects.live()
-        backend = get_search_backend()
+        if search_model:
-
+            try:
-        # DB search. Since this backend can't handle inheritance or scoring,
+                # If provided a model name, try to get it
-        # search specified page types in the desired order and chain the results together.
+                model = ContentType.objects.get(model=search_model).model_class()
-        # This provides better search results than simply searching limited fields on CoderedPage.
+                results = results.type(model)
-        db_models = []
+            except ContentType.DoesNotExist:
-        if backend.__class__ == db.SearchBackend:
+                # Maintain existing behavior of only returning objects if the page type is real
-            for model in get_page_models():
+                results = None
-                if model.search_db_include:
+
-                    db_models.append(model)
+        # get and paginate results
-            db_models = sorted(db_models, reverse=True, key=lambda k: k.search_db_boost)
-
-        if backend.__class__ == db.SearchBackend and db_models:
-            for model in db_models:
-                # if search_model is provided, only search on that model
-                if not search_model or search_model == ContentType.objects.get_for_model(model).model:  # noqa
-                    curr_results = model.objects.live().search(search_query)
-                    if results:
-                        results = list(chain(results, curr_results))
-                    else:
-                        results = curr_results
-
-        # Fallback for any other search backend
-        else:
-            if search_model:
-                try:
-                    model = ContentType.objects.get(model=search_model).model_class()
-                    results = model.objects.live().search(search_query)
-                except ContentType.DoesNotExist:
-                    results = None
-            else:
-                results = CoderedPage.objects.live().order_by('-last_published_at').search(search_query)  # noqa
-
-        # paginate results
         if results:
         if results:
+            results = results.search(search_query)
             paginator = Paginator(results, GeneralSettings.for_request(request).search_num_results)
             paginator = Paginator(results, GeneralSettings.for_request(request).search_num_results)
             page = request.GET.get('p', 1)
             page = request.GET.get('p', 1)
             try:
             try:
@@ -93,9 +68,6 @@ def search(request):
             except InvalidPage:
             except InvalidPage:
                 results_paginated = paginator.page(1)
                 results_paginated = paginator.page(1)
 
 
-        # Log the query so Wagtail can suggest promoted results
-        Query.get(search_query).add_hit()
-
     # Render template
     # Render template
     return render(request, 'coderedcms/pages/search.html', {
     return render(request, 'coderedcms/pages/search.html', {
         'request': request,
         'request': request,

+ 7 - 1
docs/features/searching.rst

@@ -32,10 +32,16 @@ For example, to enable search filtering by Blog or by Products in addition to Al
 
 
 Would enable the following filter options on the search page: All Results, Blog, Products.
 Would enable the following filter options on the search page: All Results, Blog, Products.
 
 
+
 Search fields
 Search fields
 -------------
 -------------
 
 
-If using the Wagtail DatabaseSearch backend (default), only page Title and Search Description
+.. deprecated:: 0.25
+
+   The following behavior and the ``search_db_*`` options were removed in 0.25.
+   Use the `Wagtail search parameters <https://docs.wagtail.org/en/stable/reference/contrib/searchpromotions.html#module-wagtail.contrib.search_promotions>`_ instead.
+
+If using the ``wagtail.search.backends.db`` backend (removed in Wagtail 3.0), only page Title and Search Description
 fields are searched upon. This is due to a limitation in the DatabaseSearch backend;
 fields are searched upon. This is due to a limitation in the DatabaseSearch backend;
 other backends such as PostgreSQL and Elasticsearch will search on additional specific fields
 other backends such as PostgreSQL and Elasticsearch will search on additional specific fields
 such as body, article captions, etc. To enable more specific searching while still using the
 such as body, article captions, etc. To enable more specific searching while still using the