Wagtail Tutorial Series:
To get the latest learning resource for Wagtail 4, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Add Bootstrap Theme to Wagtail
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add pagination component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics:
Objective
By the end of this chapter, you should be able to:
- Understand how Wagtail full text search works.
- Display search keywords on the search result page.
Prerequisites
Update wagtail_bootstrap_blog/urls.py
- Remove
path('search/', search_views.search, name='search'),
- Remove
from search import views as search_views
Backend
Update wagtail_bootstrap_blog/settings/base.py
INSTALLED_APPS = [
# code omitted for brevity
'wagtail.contrib.postgres_search',
]
WAGTAILSEARCH_BACKENDS = {
'default': {
'BACKEND': 'wagtail.contrib.postgres_search.backend',
},
}
Notes:
- We added
wagtail.contrib.postgres_search
to theINSTALLED_APPS
- We config
WAGTAILSEARCH_BACKENDS
to use thepostgres_search.backend
$ docker-compose exec web python manage.py migrate
Notes:
- This would create a db table for
postgres search backend
to store theindex data
Model
Update blog/models.py
from wagtail.search import index
class PostPage(Page):
search_fields = Page.search_fields + [
index.SearchField('title'),
index.SearchField('body'),
]
Notes:
- Here we defined
search_fields
so Wagtail would know which fields need to be processed when indexing. - Do not forget to put
from wagtail.search import index
at the top of the file.
After this is done, let's run command to build the index.
$ docker-compose up -d
$ docker-compose logs -f
$ docker-compose exec web python manage.py update_index
Notes:
- We need to do this manually when we first setup
postgres search backend
- The command would extract text from the
search_fields
, and write index to theindex entry
table. - By default, when we do db operation (create page, edit page), the
search index
would also be updated. That is why the search function can work with latest data even we do not runupdate_index
command.
Let's test in the Django shell.
$ docker-compose exec web python manage.py shell
>>> from blog.models import PostPage
# run search method to do full text search
>>> PostPage.objects.search('wagtail')
<SearchResults [<PostPage: PostPage4>, <PostPage: PostPage3>, <PostPage: PostPage2>, <PostPage: PostPage1>]>
Notes:
- The
postgres search backend
would transform thesearch
method to search the Postgresindex entry
table. - Different search backends would generate different query code here.
Route
Update blog/models.py
class BlogPage(RoutablePageMixin, Page):
# code omitted for brevity
@route(r"^search/$")
def post_search(self, request, *args, **kwargs):
search_query = request.GET.get("q", None)
self.posts = self.get_posts()
if search_query:
self.posts = self.posts.search(search_query)
return self.render(request)
Notes:
- We added a route to the
BlogPage
, which handle thesearch
request. - We get the keywords from the querystring
q
Update wagtail_bootstrap_blog/templates/blog/components/sidebar.html
{% load blogapp_tags wagtailroutablepage_tags %}
<div class="col-md-4">
{% if blog_page %}
<div class="card my-4">
<h5 class="card-header">Search</h5>
<div class="card-body">
<form role="search" method="get" class="form-search" action="{% routablepageurl blog_page "post_search" %}">
<div class="input-group">
<input type="text" class="form-control search-query" name="q" placeholder="Search…" title="Search for:" />
<span class="input-group-btn">
<button class="btn btn-secondary" type="submit">Go!</button>
</span>
</div>
</form>
</div>
</div>
{% categories_list %}
{% tags_list %}
{% endif %}
</div>
Notes:
- In the sidebar, we added a search form above the Category widget.
- We can even put the code in a new template file to make the
sidebar.html
cleaner. - The
form action
is the url of thepost_search
route. - When we submit the form, it would send
GET
request which has url likehttp://127.0.0.1:8000/search/?q=wagtail
Display search keywords
To display search keywords on the search result page.
Update blog/models.py
class BlogPage(RoutablePageMixin, Page):
@route(r"^(\d{4})/$")
@route(r"^(\d{4})/(\d{2})/$")
@route(r"^(\d{4})/(\d{2})/(\d{2})/$")
def post_by_date(self, request, year, month=None, day=None, *args, **kwargs):
self.search_type = 'date'
self.search_term = year
self.posts = self.get_posts().filter(post_date__year=year)
if month:
df = DateFormat(datetime.date(int(year), int(month), 1))
self.search_term = df.format('F Y')
self.posts = self.posts.filter(post_date__month=month)
if day:
self.search_term = date_format(datetime.date(int(year), int(month), int(day)))
self.posts = self.posts.filter(post_date__day=day)
return self.render(request)
@route(r'^tag/(?P<tag>[-\w]+)/$')
def post_by_tag(self, request, tag, *args, **kwargs):
self.search_type = 'tag'
self.search_term = tag
self.posts = self.get_posts().filter(tags__slug=tag)
return self.render(request)
@route(r'^category/(?P<category>[-\w]+)/$')
def post_by_category(self, request, category, *args, **kwargs):
self.search_type = 'category'
self.search_term = category
self.posts = self.get_posts().filter(categories__blog_category__slug=category)
return self.render(request)
@route(r"^search/$")
def post_search(self, request, *args, **kwargs):
search_query = request.GET.get("q", None)
self.posts = self.get_posts()
if search_query:
self.search_term = search_query
self.search_type = 'search'
self.posts = self.posts.search(search_query)
return self.render(request)
Notes:
- We added
search_term
to make it represent thesearch keywords
,category
ortag
- We added
search_type
to make it represent the filter type.
Update wagtail_bootstrap_blog/templates/blog/blog_page.html
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags blogapp_tags %}
{% block content %}
{% if page.search_term %}
<div class="alert alert-success">
Posts for <span>{{ page.search_type }}: {{ page.search_term }}</span>
</div>
{% endif %}
......
{% endblock %}
Notes:
- If the
page.search_term
is not None, display the filter messages.
Performance Notes:
From Wagtail doc, there are multiple search backends for us to use
- If the indexed data is huge, please check
Elasticsearch backend
- The default
database backend
have performance issue when dealing with big data. - If you use
Postgres
and the indexed data is not huge, thenpostgres search backend
is the best option. - If you use
Mysql
, you can check wagtail-whoosh, I am the maintainer of this repo. If it can not work for you, then you might need to useElasticsearch backend
Wagtail Tutorial Series:
To get the latest learning resource for Wagtail 4, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Add Bootstrap Theme to Wagtail
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add pagination component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics: