Django Search (with Q objects) Tutorial

stackpython
9 min readOct 17, 2020

Assuming you are implementing your django blog post with search form(Bootstrap) or any HTML forms, but do not know how make it functional. This post is created for you.

Prerequisites

YOU SHOULD

  • Get familiar with basic Django, and its structures
  • Get familiar with Bootstrap, and basic frontend programming languages , HTML, CSS.

How does search work ?(Brief)

We type any word e.g. “django” to our search form from a navbar section, then a client(Web browser) sends request to a server, a server makes query to a database. Based on a query, if found, response a post we previously made query to show on a web page

For instance, look at the images below captured from Django official website’s documentation

It has a search form

Search form on Django documentation

4 Steps closer

  • We typed “admin” on this form to get desired admin results related to the word admin
  • q (String field) is sent to a Django server with a value “admin”
  • Django server receives q variable, and navigate to a database for seaching any posts related to, or contained with “admin
  • Then send posts back to a client showing all posts related to admin on a web page

Look at this url → search/?q=admin

While…

search: Location of search form indicated that search swill start executing after this url

? : A Separator or Indicator used to indicate that after this sign is a query string part

q: A field

=: Equals sign used to encapsulate value with q(string parametr)

admin: Value we typed to search for the result of “admin

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

Searching for “admin” then the list of admin displayed

→ https://docs.djangoproject.com/en/3.1/search/?q=admin

Let’s get started

In this article, to reduce time consuming on writing, I did not show you how to build a project from scratch e.g. creating virtual environment, installing Django, etc. As mentioned above, I assumed that you should get familiar with Django beforehand.

Create Django project

The first step is to start a project (Assuming you all have created a directory to contain a project, virtual environment, and installed Django already)

django-admin startproject myproject

then start an app

python manage.py startapp myapp

Now we already have our first project and app

Let’s create our first Django model

models.py

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

Register the app

settings.py

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)

The next step is to migrate our model into the database

python manage.py makemigrations
Successfully make migrations

Then

python manage.py migrate
Migrating to our database

Now we have all tables containing in a single file called db.sqlite3

SQLite database file was built

Create a superuser to access an admin site

Type the following command to create a superuser

python manage.py createsuperuser
Create superuser on terminal

Register a Django model

Go to admin.py file

admin.py

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

Now we just finish registering our model, let’s get into Django admin site to add some posts.

Django Admin Site

Make sure your server is still running, then navigate to an admin site by placing this url http://127.0.0.1:8000/admin

Then use previous username and password we just created

Django administration

After that

Django admin

Add some posts

Save and add other posts

Finally, we have 3 simple posts

Added 3 posts to test

Creating Django Templates

from your app directory, the path will look like this

myapp/templates/myapp/index.html

index.html

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

Now we already created an HTML file to display our posts

Django Render Template

It’s time to render an HTML file to show on a web browser.

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

Django URL Routing

We just created a simple function only rendering an HTML file to display to a client side, so now we have to map the function with urls we are designing now. Keep in mind that urls.py does not come default with Django, so we should create a new one.

myapp/urls.py

from django.urls import pathfrom . import viewsurlpatterns = [    path('', views.index, name="index")]

We have one step left, to register myapp.urls module into myproject’s urls

myproject/urls.py

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

Now we have a simple web page, but it does not look good enought, and not responsive as well. So Bootstrap can help us with 2 very easy steps by adding the following block code into index.html

  • Add Bootstrap CDN
  • Add Bootstrap Navbar

Browse to Bootstrap official website to get Bootstrap CDN

click here

Bootstrap CDN
Bootstrap Navbar

Add Bootstrap CDN and Navbar to our page

index.html

<!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>

Refresh a web page, we will see

Home page with Bootstrap Navbar

Django QuerySet(Query data from a database)

The previous step We just rendered a template(HTML file) to display on a web browser but did not send any data back. Now it’s time to do that.

views.py

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})

Loop to show all posts

index.html

<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

Descending order of our posts

We can arrange our posts by using order_by(“-date_create”) with negative sign “-” so that the lastest post will be showed on the top of a web page

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

Adding search string parameter and url endpoint to search for

  • adding search string parameter called “search”
name="search"
  • Add url endpoint to an action attribute of form after submitting a form
action="{% url 'index' %}"

index.html

Now, the form will look like this

<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 by typing a desired word

Test searching

Search with Q objects

This is the core section of this article, we use Q objects to make more complex queries following

  • Use & (AND) operator to search for more than 2 fields. For example, when searching title & content, both should be true, when searching for a word such as “python”, so it(python) should be contained in both title and content fields
  • Use | (OR) operator to search for only one field. For example, when searching title | content, both don’t have to be true, only one is okay, when searching for a word such as “python”, so it(python) doesn’t have to be contained in both title and content fields

Do not forget to import Q objects

For more details about Q objects, you can read from the document here

views.py

from django.shortcuts import renderfrom .models import Postfrom django.db.models import Q  # New

In index function, create a new variable called “search_post” to get url parameter from a form submitting from a web page

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.

Afterwards, add if-else conditional statements to the function

if getting a search param from client side, then do something, else does a default return data to a web page showing all posts

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

It founds because I typed “flask” and of course, Flask lives both title and content fields

Not found

Not get a result because “python” lives only a title field, but we use & (AND) for our searching condition

Not found

Not get a result because “django” lives only a title field, but we use & (AND) for our searching condition

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

Conclusions

What we’ve learned in this article are

  • How search works
  • Basic Django MTV
  • Understanding Q objects
  • How to implement our existing search form with Django Q objects

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.

Sonny STACKPYTHON

Follow me on

Facebook

IG

My recommended article on this topic (Django search)

Django Search Tutorial

Django Search with Q objects (My YouTube Channel)

References

Complex lookups with Q objects

Query string

--

--