← Back to Django Course | Chapter 6: Advanced ORM Queries | Lesson 3 of 9

Chaining QuerySets

Chaining querysets is like adding filters to a search step by step, where each new filter narrows down the results a bit more.

What Is Chaining?

Each QuerySet method like filter() or order_by() returns another QuerySet, so you can call another method right after it, building up a query piece by piece.

Example: What Is Chaining?

This first narrows results to tutorials in the Tutorial category, then sorts just those results alphabetically.

markup
from core.models import Tutorial

# Chain filter() and order_by() together
Tutorial.objects.filter(category='Tutorial').order_by('title')
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Lazy Evaluation

Django doesn't actually query the database while you're chaining methods — it waits until you do something that needs the real data, like looping over the queryset.

Note: This laziness lets you build complex queries efficiently before running them.

Example: Lazy Evaluation

The database is only hit when the for loop actually iterates over qs, not when it was built.

markup
from core.models import Tutorial

qs = Tutorial.objects.filter(category='Forms')  # no query yet
qs = qs.order_by('title')  # still no query
for t in qs:  # query runs here
    print(t.title)
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Combining filter() and exclude()

filter() keeps matching rows, while exclude() removes matching rows — chaining them lets you include one condition and rule out another.

Example: Combining filter() and exclude()

The result only includes published tutorials from the Tutorial category — both conditions apply together.

markup
from core.models import Tutorial

# Tutorials in 'Tutorial' category, excluding drafts
Tutorial.objects.filter(category='Tutorial').exclude(is_published=False)
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}
Common Mistakes
  1. Thinking each chained call runs a separate database query immediately, when really they combine into one query that runs only when evaluated.
  2. Reassigning the same variable name across chained calls and losing track of which filters are still active.
  3. Calling .filter() after already slicing the queryset with [:n], which Django doesn't allow.
Chapter Summary
  • QuerySet methods like filter(), exclude(), and order_by() return new QuerySets, so they can be chained.
  • Chaining doesn't hit the database until the queryset is evaluated, e.g. by looping over it or printing it.
  • Chained filters combine with AND logic by default.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.