Django Search (with Q objects) Tutorial

Prerequisites

How does search work ?(Brief)

Search form on Django documentation

Look at this url → search/?q=admin

Note: This is a query string q=admin composed with field and value pair

Searching for “admin” then the list of admin displayed

Let’s get started

Create Django project

django-admin startproject myproject
python manage.py startapp myapp

Let’s create our first Django model

from django.db import models# Create your models here.class Post(models.Model):    title = models.CharField(max_length=80)    content = models.TextField()    date_created = models.DateTimeField(auto_now_add=True)    date_updated = models.DateTimeField(auto_now=True)
def __str__(self): return self.title

Note: we use auto_now_add=True to automatically update time after creating a post, this will show only one time, and auto_now=True to automatically update after editing a post, and it changes every time we edit a post

INSTALLED_APPS = [    'myapp', # New    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages',    'django.contrib.staticfiles',]

Note: In this post, I use SQLite as a default database, if you want to chose the most recommended DB for Django “PostgreSQL” you can follow my previous article How to Start Django Project with a Database(PostgreSQL)

python manage.py makemigrations
Successfully make migrations
python manage.py migrate
Migrating to our database
SQLite database file was built

Create a superuser to access an admin site

python manage.py createsuperuser
Create superuser on terminal

Register a Django model

from django.contrib import adminfrom .models import Post  # New# Register your models here.admin.site.register(Post)  # New

Django Admin Site

Django administration
Django admin
Save and add other posts
Added 3 posts to test

Creating Django Templates

<!DOCTYPE html>
<head><title>Home</title></head>
<body> <h1>Hello Django</h1></body>

Django Render Template

from django.shortcuts import renderfrom .models import Post  # Create your views here.def index(request):
return render(request, 'myapp/index.html')

Django URL Routing

from django.urls import pathfrom . import viewsurlpatterns = [    path('', views.index, name="index")]
from django.contrib import adminfrom django.urls import path, include  # Newurlpatterns = [path('admin/', admin.site.urls),path('', include('myapp.urls'))  # New]
Home page

Integrating Bootstrap Navbar to our Django site

Bootstrap CDN
Bootstrap Navbar
<!DOCTYPE html><head>    <!-- Boostrap CSS CDN -->    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">    <title>Home</title></head>
<body><!-- Boostrap Navbar --><nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
<!-- Our content --> <h1>Hello Django</h1> <!-- Boostrap JS CDN --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
</body>
Home page with Bootstrap Navbar

Django QuerySet(Query data from a database)

from django.shortcuts import renderfrom .models import Post  # New# Create your views here.def index(request):
# Query all posts posts = Post.objects.all()
return render(request, 'myapp/index.html', {'posts': posts})
<div class="container">    <h1>Hello Django</h1><br>    {% for post in posts %}    <h1>{{forloop.counter}}. {{post.title}}</h1>    <p>{{post.content}}</p>    <p>Date Created: {{post.date_created}}</p>    <p>Last Update: {{post.date_updated}}</p>    {% endfor %}</div>

Note: {{forloop.counter}} used to count posts ascending order. You don’t have to use it in this post but you can integrate it with a table that used to represent a number of items. You can read more here

Posts list
posts = Post.objects.all().order_by("-date_created")
The lastest post will be on top of the page orderly

Implementing Django Search with Bootstrap form

Test seaching
name="search"
action="{% url 'index' %}"
<form class="form-inline my-2 my-lg-0" action="{% url 'index' %}">    <input class="form-control mr-sm-2" type="search"   placeholder="Search" aria-label="Search" name="search">    <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button></form>
Bootstrap navbar search form
Test searching

Search with Q objects

Do not forget to import Q objects

from django.shortcuts import renderfrom .models import Postfrom django.db.models import Q  # New
search_post = request.GET.get('search')

We use GET method in this context because we did not make change to our resource(database) only show search result, so GET method is used.

if search_post:    posts = Post.objects.filter(Q(title__icontains=search_post) & Q(content__icontains=search_post)else:    # If not searched, return default posts    posts = Post.objects.all().order_by("-date_created")
views.py
Post found
Not found
Not found

Congratulations !! you just finished implementing a search form to search posts from a database

Conclusions

If this article is helpful, please clap to support me continuing my good work, and do not hesitate to drop your comment below sharing me your opinion or problem. See you again next article.

--

--