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
- 1. Introduction
- 2. Installation and setup
- 3. How things work
- 4. The first Django app
- 5. Your first view
- 6. Your first template
- 7. Testing our site
- 8. Extending templates
- 9. Your first model
- 10. The admin interface
- 11. Showing contents from the model
- 12. Another app (category)
- 13. Connecting two models
- 14. Show list of categories
- 15. Category detail page
- 16. Separate url files and why
- 17. Adding tasks in the front end
- 18. Editing tasks
- 19. Completing and deleting tasks
- 20. Adding a simple search