Bird
Raised Fist0
Djangoframework~10 mins

Chaining querysets in Django - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - Chaining querysets
Start with first queryset
Apply filter or operation
Get second queryset
Chain with first queryset
Evaluate combined queryset
Return combined results
This flow shows how two or more querysets are combined by chaining filters or operations before evaluation.
Execution Sample
Django
qs1 = Book.objects.filter(author='Alice')
qs2 = Book.objects.filter(year__gte=2020)
combined_qs = qs1 | qs2
results = combined_qs.distinct()
This code chains two querysets to get books by author Alice or published from 2020 onwards, then removes duplicates.
Execution Table
StepActionQueryset StateResult Preview
1Create qs1 with filter author='Alice'qs1: SELECT * FROM Book WHERE author='Alice'Books by Alice
2Create qs2 with filter year >= 2020qs2: SELECT * FROM Book WHERE year >= 2020Books from 2020 or later
3Chain qs1 and qs2 with | operatorcombined_qs: qs1 OR qs2Books by Alice OR from 2020+
4Call distinct() on combined_qscombined_qs.distinct()Unique books matching either condition
5Evaluate combined_qs.distinct()Query runs on DBFinal list of unique books
6EndQuerysets evaluatedResults ready for use
💡 Querysets are lazy; evaluation happens at step 5 when results are needed.
Variable Tracker
VariableStartAfter Step 1After Step 2After Step 3After Step 4Final
qs1NoneQueryset filtered by author='Alice'SameSameSameSame
qs2NoneNoneQueryset filtered by year >= 2020SameSameSame
combined_qsNoneNoneNoneChained qs1 OR qs2distinct() appliedEvaluated results
Key Moments - 3 Insights
Why doesn't the database query run when we create or chain querysets?
Because Django querysets are lazy; they build the query but only run it when you actually need the data, like at step 5 in the execution table.
What does the | operator do when chaining querysets?
It combines two querysets with an OR condition, meaning results matching either queryset are included, as shown in step 3.
Why do we call distinct() after chaining querysets?
Because chaining with | can cause duplicates if some records appear in both querysets; distinct() removes these duplicates, as in step 4.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what does combined_qs represent after step 3?
ABooks by Alice AND from 2020 or later
BBooks only by Alice
CBooks by Alice OR from 2020 or later
DBooks only from 2020 or later
💡 Hint
Check step 3 in the execution table where combined_qs is described as qs1 OR qs2.
At which step does the actual database query run?
AStep 5
BStep 3
CStep 2
DStep 1
💡 Hint
Look at the exit note and step 5 in the execution table where evaluation happens.
If we remove distinct() at step 4, what might happen to the results?
AResults will be empty
BResults may include duplicates
CResults will only include books by Alice
DResults will only include books from 2020 or later
💡 Hint
Refer to key moment about why distinct() is called after chaining.
Concept Snapshot
Chaining querysets lets you combine filters using operators like | (OR).
Querysets are lazy; queries run only when results are needed.
Use distinct() to remove duplicates after chaining.
Example: combined_qs = qs1 | qs2
Evaluate with list(combined_qs) or iteration.
Full Transcript
Chaining querysets in Django means combining two or more querysets to get results matching any of their conditions. Querysets are lazy, so creating or chaining them does not run a database query immediately. The | operator combines querysets with an OR condition. After chaining, duplicates can appear, so calling distinct() removes them. The actual database query runs only when you access the results, like iterating or converting to a list. This approach helps build complex queries step-by-step without hitting the database multiple times.

Practice

(1/5)
1. What does chaining querysets in Django allow you to do?
easy
A. Change the original queryset directly with each filter
B. Run multiple queries at the same time
C. Build complex database queries step by step without changing the original queryset
D. Automatically save changes to the database

Solution

  1. Step 1: Understand queryset chaining

    Chaining querysets means applying filters or other queryset methods one after another, each returning a new queryset.
  2. Step 2: Effect on original queryset

    Each filter returns a new queryset and does not modify the original queryset, allowing step-by-step building.
  3. Final Answer:

    Build complex database queries step by step without changing the original queryset -> Option C
  4. Quick Check:

    Chaining querysets = build stepwise [OK]
Hint: Remember: filters return new querysets, original stays unchanged [OK]
Common Mistakes:
  • Thinking filters modify the original queryset
  • Believing chaining runs multiple queries simultaneously
  • Confusing queryset chaining with saving data
2. Which of the following is the correct way to chain querysets in Django?
easy
A. MyModel.objects.filter(active=True).exclude(age__lt=18)
B. MyModel.objects.filter(active=True).filter(age__lt=18).get()
C. MyModel.objects.filter(active=True).filter(age__lt=18).save()
D. MyModel.objects.filter(active=True).filter(age__lt=18).update()

Solution

  1. Step 1: Check chaining syntax

    Chaining querysets means applying filters or other queryset methods one after another, returning new querysets.
  2. Step 2: Identify invalid methods

    Methods like save() and update() are not queryset chaining methods; get() returns a single instance, not suitable for chaining.
  3. Final Answer:

    MyModel.objects.filter(active=True).exclude(age__lt=18) -> Option A
  4. Quick Check:

    Correct chaining uses filter/exclude methods [OK]
Hint: Chain filters and excludes; avoid save() or update() in chaining [OK]
Common Mistakes:
  • Using save() or update() in queryset chains
  • Using get() after filters
  • Confusing queryset methods with model instance methods
3. Given the code:
qs = MyModel.objects.filter(active=True)
qs2 = qs.filter(age__gte=18)
qs3 = qs2.exclude(name__startswith='A')

What does qs3 contain?
medium
A. Active MyModel objects aged 18 or older whose names do not start with 'A'
B. All MyModel objects regardless of filters
C. Only MyModel objects with names starting with 'A'
D. Active MyModel objects younger than 18

Solution

  1. Step 1: Analyze first filter

    qs filters objects where active=True.
  2. Step 2: Analyze second filter

    qs2 further filters qs to include only those with age >= 18.
  3. Step 3: Analyze exclude

    qs3 excludes objects from qs2 whose name starts with 'A'.
  4. Final Answer:

    Active MyModel objects aged 18 or older whose names do not start with 'A' -> Option A
  5. Quick Check:

    Filters + exclude = refined queryset [OK]
Hint: Read filters stepwise to understand final queryset content [OK]
Common Mistakes:
  • Ignoring the exclude step
  • Mixing up filter and exclude logic
  • Assuming qs3 includes names starting with 'A'
4. What is wrong with this queryset chaining?
qs = MyModel.objects.all()[:10]
qs = qs.filter(active=True)
medium
A. You cannot slice querysets in Django
B. There is no problem; this is valid chaining
C. The filter method should come before all()
D. Slicing before filtering breaks chaining; filter cannot be applied after slicing

Solution

  1. Step 1: Understand slicing effect

    Slicing a queryset (like [:10]) evaluates it and returns a list, not a queryset.
  2. Step 2: Applying filter after slicing

    Since qs is now a list, calling filter() on it causes an error or unexpected behavior.
  3. Final Answer:

    Slicing before filtering breaks chaining; filter cannot be applied after slicing -> Option D
  4. Quick Check:

    Slice first = no chaining [OK]
Hint: Always filter before slicing to keep queryset chaining intact [OK]
Common Mistakes:
  • Slicing before filtering
  • Assuming slicing returns a queryset
  • Trying to chain after slicing
5. You want to get all active users aged 18 or older, but exclude those whose names start with 'A' or 'B'. Which queryset chaining is correct?
hard
A. MyModel.objects.filter(active=True).filter(age__gte=18).exclude(name__startswith=['A', 'B'])
B. MyModel.objects.filter(active=True).filter(age__gte=18).exclude(name__startswith='A').exclude(name__startswith='B')
C. MyModel.objects.filter(active=True).exclude(name__startswith='A', name__startswith='B').filter(age__gte=18)
D. MyModel.objects.filter(active=True, age__gte=18).exclude(name__startswith='A' or 'B')

Solution

  1. Step 1: Filter active and age

    Use two filters or one combined filter to get active users aged 18 or older.
  2. Step 2: Exclude names starting with 'A' and 'B'

    Exclude separately for 'A' and 'B' because exclude(name__startswith='A' or 'B') is invalid syntax and exclude(name__startswith=['A', 'B']) is not supported.
  3. Final Answer:

    MyModel.objects.filter(active=True).filter(age__gte=18).exclude(name__startswith='A').exclude(name__startswith='B') -> Option B
  4. Quick Check:

    Chain filters then multiple excludes correctly [OK]
Hint: Chain filters first, then exclude each condition separately [OK]
Common Mistakes:
  • Using invalid exclude syntax with 'or' inside
  • Trying to exclude with a list in startswith
  • Mixing filter and exclude order incorrectly