Adding a simple search - 30 days of Django

Adding a simple search - 30 days of Django

/ #Django


Let's make a simple search for our task manager so it get easier to find your tasks.

All of our tasks can have titles and descriptions. Let's say that you have set up over 100 tasks. It wouldn't be very effective to go through the whole list when you're looking for a specific task. So let's make it possible to search.

The first thing we want is a new view. Let's put it in the task/views.py file.

from django.db.models import Q # New, 1
...

def search(request):
    query = request.GET.get('query', '')
    
    if query:
        tasks = Task.objects.filter(Q(title__icontains=query) | Q(description__icontains=query))
    else:
        tasks = []
    
    return render(request, 'task/search.html', {'query': query, 'tasks': tasks})

First, I imported a new function from Django. This let's ut build a little bit more advanced QuerySets.

Then I create the new search view. We get the "query" or search term from the url. If it exists, we get the tasks from the database by using the new Q function. Here we check if the title field contains the word we are searching for or if the description contains it. "icontains" means that it ignores capital/lowercase letters. If you used just "contains", you would have to write the letters 100% correctly.

Let's add this view to the urls.py file:

from django.urls import path

from . import views

urlpatterns = [
    path('', views.frontpage, name='frontpage'),
    path('edit_task/<int:pk>/', views.edit_task, name='edit_task'),
    path('mark_completed/<int:pk>/', views.mark_completed, name='mark_completed'),
    path('delete_task/<int:pk>/', views.delete_task, name='delete_task'),
    path('search/', views.search, name='search'), # New
]

If you tested this in your browser now, you would get a "template does not exist error". So let's create it now.

{% extends 'task/base.html' %}

{% block content %}
    <div class="frontpage">
        <h1>Search</h1>

        <h2>You searched for "{{ query }}"</h2>

        {% for task in tasks %}
            <div>
                <p>{{ task.title }}</p>
            </div>
        {% empty %}
            <p>No tasks matched your query...</p>
        {% endfor %}
    </div>
{% endblock %}

As you can see, a lot of this is copied from the front page template. I added one new tag here "{% empty %}". This checks if the list is empty or not. So if you search for something that doesn't exists, you will see the "No tasks matched your query..." message instead.

Great, now the search functionality is implemented and is hopefully working. Let's add a simple search bar to the top of base.html.

<doctype html!>
<html>
    <head>
        <title>Toodoo</title>
    </head>

    <body>
        <nav>
            The menu
        </nav>

        <div>
            <form method="get" action="{% url 'search' %}">
                <input type="search" name="query" placeholder="Find a task...">
                <button>Search</button>
            </form>
        </div>

        {% block content %}
        {% endblock %}

        <footer>
            The footer
        </footer>
    </body>
</html>

When we use forms with the method attribute set to "get" instead of "post", we do not need a csrf token.

Nice! You can now test that everything is working as expected :-)

Table of contents

Comments

No comments yet...

Add comment

Newsletter

Subscribe to my weekly newsletter. One time per week I will send you a short summary of the tutorials I have posted in the past week.