A search system allows users to enter keywords and find matching records from a database. Django provides powerful QuerySet methods that can be combined with search forms to build a simple and useful search feature.
A search system is commonly used for students, products, employees, customers, blog posts, courses, and other database records.
contains and icontains can be used to create a
basic search system.
A search system allows users to enter a keyword and retrieve matching records from a database.
For example, a student management system can allow users to search by student name:
Search: Rahul
The system can then display students whose names contain "Rahul".
Search makes large collections of records easier to use.
The icontains lookup is commonly used for a
case-insensitive search.
students = Student.objects.filter(
name__icontains="rahul"
)
This can find names containing the word "rahul" without requiring the exact capitalization.
The contains lookup searches for records containing the
specified text.
students = Student.objects.filter(
name__contains="Rahul"
)
For general user search, icontains is often more convenient
because it performs a case-insensitive lookup.
A search form commonly sends the keyword using the GET method.
For example:
/students/?q=rahul
The view can read the search keyword using:
query = request.GET.get("q", "")
A basic student search view can look like this:
from django.shortcuts import render
from .models import Student
def student_search(request):
query = request.GET.get("q", "")
students = Student.objects.filter(
name__icontains=query
)
return render(
request,
"students.html",
{
"students": students,
"query": query
}
)
A simple HTML form can be used to accept the search keyword.
<form method="get">
<input
type="text"
name="q"
placeholder="Search student"
>
<button type="submit">
Search
</button>
</form>
The name="q" attribute allows Django to retrieve the search
value using request.GET.get("q").
After filtering the QuerySet, loop through the results in the template.
{% for student in students %}
<p>
{{ student.name }}
</p>
{% empty %}
<p>
No students found.
</p>
{% endfor %}
The same technique can be used to search an email field.
students = Student.objects.filter(
email__icontains=query
)
This returns records where the email contains the search keyword.
Sometimes users should be able to search multiple fields using the same search box.
For example, a student may be searched by name or email.
from django.db.models import Q
students = Student.objects.filter(
Q(name__icontains=query) |
Q(email__icontains=query)
)
Django's Q objects allow multiple conditions to be combined
with logical operators.
from django.db.models import Q
students = Student.objects.filter(
Q(name__icontains=query) |
Q(email__icontains=query)
)
The | operator represents OR.
Suppose a Student model contains both name and
course.
students = Student.objects.filter(
Q(name__icontains=query) |
Q(course__icontains=query)
)
The search can now match either the student name or course.
Integer fields require special handling when the search value may contain non-numeric text.
For example, if students have an integer student ID, you should validate the input before using an exact numeric lookup.
student_id = request.GET.get(
"student_id",
""
)
Validate the value before converting it to an integer.
The exact lookup searches for an exact value.
students = Student.objects.filter(
name__exact=query
)
For general keyword search, icontains is usually more flexible
because it can find a keyword inside a larger value.
The startswith lookup searches for values beginning with
specific text.
students = Student.objects.filter(
name__startswith=query
)
For a case-insensitive version, use istartswith.
students = Student.objects.filter(
name__istartswith=query
)
The endswith lookup searches for values ending with specific
text.
students = Student.objects.filter(
name__endswith=query
)
Use iendswith for a case-insensitive version.
Search results can be ordered using order_by().
students = Student.objects.filter(
name__icontains=query
).order_by("name")
This displays matching students alphabetically by name.
Search can be combined with Django pagination.
from django.core.paginator import Paginator
students = Student.objects.filter(
name__icontains=query
).order_by("name")
paginator = Paginator(
students,
10
)
page_number = request.GET.get(
"page"
)
page_obj = paginator.get_page(
page_number
)
Now the search results can be displayed 10 records per page.
When search and pagination are combined, the search keyword should be preserved in pagination links.
<a href="?q={{ query }}&page=2">
Page 2
</a>
Without the q parameter, clicking the next page may remove
the current search filter.
It is often useful to display all records when the search box is empty.
query = request.GET.get(
"q",
""
).strip()
if query:
students = Student.objects.filter(
name__icontains=query
)
else:
students = Student.objects.all()
The strip() method removes unnecessary spaces from the
beginning and end of the search text.
Django can also search fields from related models.
For example, if Student has a ForeignKey to Course:
students = Student.objects.filter(
course__name__icontains=query
)
The double underscore syntax allows Django to follow relationships.
A search form can be styled with Bootstrap.
<form method="get" class="mb-4">
<div class="input-group">
<input
type="text"
name="q"
value="{{ query }}"
class="form-control"
placeholder="Search student"
>
<button
type="submit"
class="btn btn-primary"
>
Search
</button>
</div>
</form>
The value attribute keeps the existing search keyword visible
after the form is submitted.
The search keyword can be displayed on the results page.
<h4>
Search results for:
{{ query }}
</h4>
This helps users understand which search is currently active.
The count() method can be used to display the number of
matching records.
total = students.count()
Pass the value to the template:
{
"students": students,
"query": query,
"total": total
}
Then display it:
Found {{ total }} students.
Multiple conditions can be combined in a search query.
students = Student.objects.filter(
Q(name__icontains=query) |
Q(email__icontains=query) |
Q(course__icontains=query)
).order_by("name")
This provides a more flexible search experience.
Q when using Q objects.__icontains in a keyword search.from django.shortcuts import render
from django.db.models import Q
from django.core.paginator import Paginator
from .models import Student
def student_search(request):
query = request.GET.get(
"q",
""
).strip()
students = Student.objects.all()
if query:
students = students.filter(
Q(name__icontains=query) |
Q(email__icontains=query) |
Q(course__icontains=query)
)
students = students.order_by("name")
paginator = Paginator(
students,
10
)
page_number = request.GET.get(
"page"
)
page_obj = paginator.get_page(
page_number
)
return render(
request,
"students.html",
{
"page_obj": page_obj,
"query": query
}
)
<form method="get">
<input
type="text"
name="q"
value="{{ query }}"
placeholder="Search"
>
<button type="submit">
Search
</button>
</form>
{% for student in page_obj %}
<p>
{{ student.name }}
</p>
{% empty %}
<p>
No results found.
</p>
{% endfor %}
{% if page_obj.has_previous %}
<a href="?q={{ query }}&page={{
page_obj.previous_page_number
}}">
Previous
</a>
{% endif %}
{% if page_obj.has_next %}
<a href="?q={{ query }}&page={{
page_obj.next_page_number
}}">
Next
</a>
{% endif %}
User enters keyword
↓
Search form sends GET request
↓
View reads request.GET
↓
QuerySet is filtered
↓
Search results are ordered
↓
Results are paginated
↓
Page object is sent to template
↓
Results are displayed
↓
User navigates through pages
A Django search system can be created by combining GET requests with QuerySet lookups.
request.GET.get() to read the search keyword.icontains for flexible text searching.Q objects to search multiple fields.startswith and endswith when required.order_by() to organize search results.Paginator for large search result sets.request.GET.get("q") can read a search keyword.icontains is useful for case-insensitive text search.Q objects can combine multiple search conditions.Question: Which Django lookup is commonly used for a case-insensitive text search?