Building a search view
Now, we will create a custom view to allow our users to search posts. First, we will need a search form. Edit the forms.py file of the blog application and add the following form:
class SearchForm(forms.Form):
query = forms.CharField()
We will use the query field to let the users introduce search terms. Edit the views.py file of the blog application and add the following code to it:
from django.contrib.postgres.search import SearchVector
from .forms import EmailPostForm, CommentForm, SearchForm
def post_search(request):
form = SearchForm()
query = None
results = []
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']
results = Post.objects.annotate(
search=SearchVector('title', 'body'),
).filter(search=query)
return render(request,
'blog/post/search.html',
{'form': form,
'query': query,
'results': results})
In the preceding view, first, we instantiate the SearchForm form. We plan to submit the form using the GET method so that the resulting URL includes the query parameter. To check whether the form is submitted, we look for the query parameter in the request.GET dictionary. When the form is submitted, we instantiate it with the submitted GET data, and we verify that the form data is valid. If the form is valid, we search for posts with a custom SearchVector instance built with the title and body fields.
The search view is ready now. We need to create a template to display the form and the results when the user performs a search. Create a new file inside the /blog/post/ template directory, name it search.html, and add the following code to it:
{% extends "blog/base.html" %}
{% block title %}Search{% endblock %}
{% block content %}
{% if query %}
<h1>Posts containing "{{ query }}"</h1>
<h3>
{% with results.count as total_results %}
Found {{ total_results }} result{{ total_results|pluralize }}
{% endwith %}
</h3>
{% for post in results %}
<h4><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h4>
{{ post.body|truncatewords:5 }}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p><a href="{% url "blog:post_search" %}">Search again</a></p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="get">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% endif %}
{% endblock %}
As in the search view, we can distinguish whether the form has been submitted by the presence of the query parameter. Before the post is submitted, we display the form and a submit button. After the post is submitted, we display the query performed, the total number of results, and the list of posts returned.
Finally, edit the urls.py file of your blog application and add the following URL pattern:
path('search/', views.post_search, name='post_search'),
Now, open http://127.0.0.1:8000/blog/search/ in your browser. You should see the following search form:
Enter a query and click on the Search button. You will see the results of the search query, as follows:
Congratulations! You have created a basic search engine for your blog.