Introduction

Django’s ORM (Object-Relational Mapper) is one of its most powerful features. It allows you to interact with your database using Python code instead of writing raw SQL.

At the heart of the ORM is the QuerySet — a powerful abstraction that lets you retrieve, filter, and manipulate data efficiently.

In this tutorial, you will go beyond the basics and learn how to:

This knowledge is essential because every Django application depends heavily on database queries.

What You Will Learn

By the end of this tutorial, you will understand:

Prerequisites

You should already know:

1. What is a QuerySet?

A QuerySet is a collection of database queries.

When you write:

Post.objects.all()

Django does NOT immediately fetch data from the database. Instead, it creates a QuerySet.

👉 A QuerySet is:

2. Basic QuerySet Operations

Get All Objects

Post.objects.all()

Get One Object

Post.objects.get(id=1)

⚠️ If no object or multiple objects exist, get() raises an error.

Filter Data

Post.objects.filter(title="Django")

Returns all posts with the exact title.

Exclude Data

Post.objects.exclude(title="Django")

Returns all posts except those with that title.

Order Data

Post.objects.order_by('created_at')

Ascending order

Post.objects.order_by('-created_at')

Descending order

3. QuerySet Chaining

One of the most powerful features is chaining.

Post.objects.filter(title__icontains='django').order_by('-created_at')

This means:

Each method returns a new QuerySet, so you can chain as many as you want.

4. Field Lookups (Very Important)

Django provides many lookup expressions.

Common Lookups

Case-insensitive search

Post.objects.filter(title__icontains='django')

Greater than

Post.objects.filter(id__gt=5)

Less than

Post.objects.filter(id__lt=10)

Range

Post.objects.filter(id__range=(1, 10))

Starts with

Post.objects.filter(title__startswith='Django')

Ends with

Post.objects.filter(title__endswith='Guide')

Date filtering

Post.objects.filter(created_at__year=2025)

5. Combining Filters

You can combine conditions using Q objects.

Example

from django.db.models import Q

Post.objects.filter(
    Q(title__icontains='django') | Q(content__icontains='python')
)

This means:

AND condition

Post.objects.filter(
    Q(title__icontains='django') & Q(content__icontains='python')
)

6. Limiting QuerySets

First object

Post.objects.first()

Last object

Post.objects.last()

Limit results

Post.objects.all()[:5]

Skip results

Post.objects.all()[5:10]

7. Checking Existence

Instead of:

if Post.objects.filter(title='Django'):

Use:

Post.objects.filter(title='Django').exists()

👉 This is faster and more efficient.

8. Counting Objects

Post.objects.count()

Counts rows directly in the database.

9. Updating Objects

Update multiple records

Post.objects.filter(title='Old').update(title='New')

Update a single object

post = Post.objects.get(id=1)
post.title = "Updated title"
post.save()

10. Deleting Objects

Delete one object

post = Post.objects.get(id=1)
post.delete()

Delete multiple objects

Post.objects.filter(title='Test').delete()

11. Aggregation

Aggregation is used to compute values like:

Example

from django.db.models import Count

Post.objects.aggregate(total_posts=Count('id'))

More Examples

from django.db.models import Avg, Max

Post.objects.aggregate(
    avg_id=Avg('id'),
    max_id=Max('id')
)

12. Annotation

Annotation adds calculated fields to each object.

Example

from django.db.models import Count
from .models import Category

Category.objects.annotate(post_count=Count('post'))

Each category will now have a post_count attribute.

13. Lazy Evaluation (Very Important)

QuerySets are lazy.

This means Django does NOT hit the database until necessary.

Example

qs = Post.objects.all()

No query yet.

But when you do:

for post in qs:
    print(post.title)

👉 Now the query runs.

When QuerySets Execute

14. Performance Optimization

Problem: N+1 Queries

Example:

for post in Post.objects.all():
    print(post.author.name)

This may cause many database queries.

Solution: select_related

Post.objects.select_related('author')

Used for ForeignKey relationships.

Solution: prefetch_related

Post.objects.prefetch_related('tags')

Used for:

Summary

MethodUse Case
select_relatedForeignKey
prefetch_relatedManyToMany

15. Using ORM in Class-Based Views

Example

class PostListView(ListView):
    model = Post

    def get_queryset(self):
        return Post.objects.filter(title__icontains='django').order_by('-created_at')

This lets you customize what data is shown.

16. Raw SQL (When Needed)

Sometimes you need raw SQL.

Post.objects.raw("SELECT * FROM blog_post WHERE id > %s", [5])

⚠️ Use this only when necessary.

17. Common Mistakes

Using get() instead of filter()

Post.objects.get(title='Django')

⚠️ Can crash if multiple results exist.

Not using exists()

if Post.objects.filter(title='Django'):

❌ Inefficient

✔️ Better:

Post.objects.filter(title='Django').exists()

Forgetting query optimization

Not using select_related or prefetch_related can slow your app.

18. Best Practices

19. Mini Project: Search System

Goal

Create a search feature.

View

class PostSearchView(ListView):
    model = Post
    template_name = 'blog/search.html'
    context_object_name = 'posts'

    def get_queryset(self):
        query = self.request.GET.get('q')
        if query:
            return Post.objects.filter(
                title__icontains=query
            )
        return Post.objects.none()

Template

<form method="get">
    <input type="text" name="q" placeholder="Search...">
    <button type="submit">Search</button>
</form>

{% for post in posts %}
    <h2>{{ post.title }}</h2>
{% empty %}
    <p>No results found.</p>
{% endfor %}

20. Summary

In this tutorial, you learned:

Mastering QuerySets is essential for building efficient Django applications.

21. Mini Quiz

1. What is a QuerySet?

A. A database table
B. A collection of queries
C. A Python list
D. A model

2. Which method filters data?

A. get()
B. filter()
C. all()
D. order_by()

3. Which method checks existence efficiently?

A. count()
B. exists()
C. len()
D. first()

4. Which method is used for ForeignKey optimization?

A. prefetch_related()
B. select_related()
C. annotate()
D. aggregate()

5. What is QuerySet behavior?

A. Immediate
B. Lazy
C. Static
D. Fixed

22. What Comes Next?

Next tutorial:

Tutorial: Relationships in Django Models
(ForeignKey, OneToOne, ManyToMany — deep understanding of data relationships)