Implementing search in Django fbv
Tags: python, django, webdev, beginners
Published: 2023-02-10 10:30:20

> So after about 2 years after I am writing this blog post.

What can I do?
I am very lazy...

After procrastinating for 2 years I've decided to finally write a blog post

---

I assume that you have some basic knowledge of Django requests and responses, like GET and POST request and URL mapping etc.

So, let's get to the point.


For example we have a model called `Book`

```python
# models.py

from django.db import models

class Book(models.Model):
    """
    A Simple Book Model
    """
    ISBN_HELP_URL = "https://www.isbn- 
    international.org/content/what-isbn"

    name = models.CharField(max_length=255)
    author = models.CharField(max_length=255)
    summary = models.TextField(blank=True, null=True)
    isbn = models.CharField(help_text=f"<a href='{ISBN_HELP_URL}' 
    target='_blank'>More Info</a>", max_length=10)
    genre = models.CharField(max_length=255, help_text="Enter a 
    book genre (e.g. Science Fiction, French Poetry etc.)")

    def __str__(self):
        return self.name

```

So we want to make a page to search for a `Book` based on the following fields:

- name
- author
- summary
- isbn
- genre

We can simply use the `Q` operator.
It is built in to Django.
It is used to Join the querysets.

Here is a simple example:

```py
# views.py

from django.db.models import Q
from django.shortcuts import render, redirect
from .models import Book

def frontpage(request):
"""
The default frontpage

Return all the books in the database.
"""
books = Book.objects.all()
   return render(request, "frontpage.html", {"books": books})

def search(request):
    """
    Searches the books based on the query.
    """
    query = request.GET.get("query", None)
    if query:
        books = Book.objects.filter(
                Q(name__icontains=query) |
                Q(author__icontains=query) |
                Q(summary__icontains=query) |
                Q(isbn__icontains=query) |
                Q(genre__icontains=query)
        )
        return render(request, "search.html", {"books": books})

    return redirect(request, "frontpage")
```
Now that looks so complicated, but it isn't
If you understand what this code is doing you can easily implement in your projects

So let's break this down into pieces.

1. The function `frontpage`, well shows the frontpage with all the Books in the database, easy.



2. The `search` function searches the books based on the query entered by the user. 
    The query can match the name, author, summary, ISBN, or genre of the book. 
    If a query is entered, the function filters the books and returns the filtered books to be displayed in the "search.html" template. 
    If no query is entered, the function redirects the user to the "frontpage".

We are firstly importing the `Q` operator from `django.db.models`.
Then we are filtering the Books based on the fields

This is the main code
```py
books = Book.objects.filter(
                Q(name__icontains=query) |
                Q(author__icontains=query) |
                Q(summary__icontains=query) |
                Q(isbn__icontains=query) |
                Q(genre__icontains=query)
        )
```

The `|` indicate that we are joining the queryset so even if the query isn't matched with the name but matched with the author it will fetch that book from the database

This double underscores and icontains represent that we also want to make the query case insensitive.

For example if we want to search for a Book named The Brief History of Time by Stephen Hawking,
We don't have to type the exact name of the book.
The Query could include the following:

-  the brief history of time
- THE BRIEF HISTORY OF TIME
- History of Time
- brief history

The possibilities could get endless.

So now let's map the views to the urls

Open the `urls.py`

```python
# urls.py

from django.contrib import admin
from django.urls import path
from myapp import views

urlpatterns = [
    path('', views.frontpage, name="frontpage"),
    path('/search/', views.search, name="search"),
    path('admin/', admin.site.urls),
]

```

Now let's move to the frontend

Create a folder in `myapp` (Your app folder) named as `templates`.
In it create two files named:
- `frontpage.html`
- `search.html`

If you wish you can use a base.html and template inheritance, but here I will be doing the simple html with no css, so it will look ugly.

```html
<!-- frontpage.html -->
<html>
  <head>
    <title>Jamshedpur Library</title
  </head>
  <body>
    <h2>Jamshedpur Library</h2>
    <p>Frontpage</p>
    <form action="{% url 'search' %}" method="GET">
    <input type="search" placeholder="search" name="query">
    <button type="submit">Search 🔍</button>
    </form>
    <ul>
      {% for book in books %}
        <li>{{ book.name }} by {{ book.author }}</li>
      {% endfor %}
    </ul>
  </body>
</html>
```

Yup it's a form which will send get request to `search` view, remember we have added a name attribute in the url patterns

Now let's see the search.html

```html
<!-- search.html -->
<html>
  <head>
    <title>Jamshedpur Library</title
  </head>
  <body>
    <h2>Jamshedpur Library</h2>
    <form action="{% url 'search' %}" method="GET">
    <input type="search" placeholder="search" name="query">
    <button type="submit">Search 🔍</button>
    </form>
    <p>You have searched for: {{ request.GET.query }}</p>
    <p>Results: {{ books.count }}</p>
    <ul>
      {% for book in books %}
        <li>{{ book.name }} by {{ book.author }}</li>
      {% endfor %}
    </ul>
  </body>
</html>
```

So here we are displaying the search results with the no. of results just as in any search engine.

*BTW I have not implemented the pagination, so the data isn't spliced into smaller chunks*

Well that's how you can implement a simple search in your Django project.

---

*Also, the letter Capital `G` looks like an arrow going in a circle.*

Thanks for reading my post.

@foxy4096 out.
Cheers ✌