Lesson 53 of 60 – Django Search System
88%

Django Search System

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.

Note: Django does not require a special search package for basic database searching. QuerySet lookups such as contains and icontains can be used to create a basic search system.

1. What is a 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".

2. Why Use Search in Django?

Search makes large collections of records easier to use.

  • Find students quickly.
  • Search products.
  • Find employees.
  • Search blog posts.
  • Search courses.
  • Search customers.
  • Combine search with pagination.
  • Combine search with filtering and ordering.

3. Basic Search Using icontains

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.

4. contains Lookup

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.

5. Getting Search Text from the URL

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", "")

6. Simple Search View

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

7. Search Form

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").

8. Displaying Search Results

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

9. Search by Email

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.

10. Search by Multiple Fields

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

11. Using Q Objects

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.

12. Searching Name or Course

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.

13. Searching an Integer Field

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.

14. Exact Search

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.

15. startswith Search

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
)

16. endswith Search

The endswith lookup searches for values ending with specific text.

students = Student.objects.filter(
    name__endswith=query
)

Use iendswith for a case-insensitive version.

17. Search with Ordering

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.

18. Search with Pagination

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.

19. Preserving Search During Pagination

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.

20. Empty Search Query

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.

21. Search Across Related Models

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.

22. Search Form with Bootstrap

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.

23. Displaying the Search Keyword

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.

24. Search Result Count

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.

25. Search with Multiple Conditions

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.

26. Common Search Mistakes

  • Forgetting to import Q when using Q objects.
  • Using the wrong field name.
  • Forgetting __icontains in a keyword search.
  • Not handling an empty search value.
  • Not preserving the search keyword during pagination.
  • Converting arbitrary search text to an integer without validation.
  • Forgetting to pass the query back to the template.
  • Using the wrong related-field lookup.

27. Complete Search View

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

28. Complete Search Template

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

29. Search System Workflow

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

30. Django Search Summary

A Django search system can be created by combining GET requests with QuerySet lookups.

  • Use request.GET.get() to read the search keyword.
  • Use icontains for flexible text searching.
  • Use Q objects to search multiple fields.
  • Use startswith and endswith when required.
  • Use related-field lookups for related models.
  • Use order_by() to organize search results.
  • Use Paginator for large search result sets.
  • Preserve search parameters when creating pagination links.
  • Handle empty and invalid search values carefully.

📌 Key Points

  • request.GET.get("q") can read a search keyword.
  • icontains is useful for case-insensitive text search.
  • Q objects can combine multiple search conditions.
  • Search can be combined with filtering and ordering.
  • Search can be combined with pagination.
  • Always preserve the search query in pagination links.
  • Handle empty search values properly.

🧠 Quick Quiz

Question: Which Django lookup is commonly used for a case-insensitive text search?