Django
QuerySets
Web Development
Python
Database Management

How to combine multiple QuerySets in Django?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Django, "combine multiple QuerySets" can mean several different things. Sometimes you want one queryset with an OR condition. Sometimes you want an SQL UNION. Other times you just want to iterate over results from different models together. The right tool depends on which of those problems you actually have.

Combining Conditions on the Same Model

If both querysets target the same model and the real goal is "records matching this condition or that condition," start with Q objects or the queryset | operator.

python
1from django.db.models import Q
2
3posts = Post.objects.filter(
4    Q(status="published") | Q(title__icontains="django")
5)

This stays a real queryset, which means you can continue chaining order_by, select_related, pagination, and other ORM operations.

For simple same-model combinations, the | operator can also work:

python
1recent = Post.objects.filter(created_at__year=2026)
2featured = Post.objects.filter(featured=True)
3
4combined = recent | featured

That is often cleaner than converting both querysets to lists and stitching them together in Python.

Using union() for Set Operations

If you specifically want SQL set operations, Django supports union(), intersection(), and difference().

python
1q1 = Author.objects.filter(active=True).values_list("email")
2q2 = Author.objects.filter(newsletter_opt_in=True).values_list("email")
3
4emails = q1.union(q2)

These methods are useful when the selected columns are compatible. The result is still queryset-like, but it is more restricted than a normal model queryset, so you should expect fewer follow-up ORM operations than in a plain filter() chain.

Combining Results From Different Models

If the querysets come from different models, they are not truly mergeable into one model queryset. In that case, itertools.chain() is a practical option.

python
1from itertools import chain
2
3articles = Article.objects.filter(published=True)
4videos = Video.objects.filter(published=True)
5
6feed_items = list(chain(articles, videos))

This gives you one iterable, but not one queryset. That distinction matters because once you switch to chain(), you lose database-level filtering, slicing, and ordering across the combined result unless you do those steps manually in Python.

Ordering a Mixed Result Set

If you chain different models together, you often still want one time-ordered feed. That means you must sort after evaluation.

python
1from itertools import chain
2
3feed_items = sorted(
4    chain(
5        Article.objects.filter(published=True),
6        Video.objects.filter(published=True),
7    ),
8    key=lambda item: item.published_at,
9    reverse=True,
10)

This works, but keep in mind that all items are now in memory. That is fine for small feeds and admin tools, but it is not something to do casually with huge datasets.

Choose the Database When Possible

As a rule, prefer database-level combination when the data shape allows it. A single SQL query or set operation usually scales better than pulling thousands of rows into Python and combining them there.

Python-side combination makes sense when:

  • the querysets come from different models
  • the databases are different
  • the merge rule is too custom for straightforward SQL

Otherwise, staying inside the ORM is usually the better choice.

Common Pitfalls

The biggest mistake is using chain() when you still need queryset behavior afterward. Once you convert to a Python iterable, you cannot keep chaining ORM methods on it.

Another mistake is using union() on incompatible querysets. SQL set operations require compatible selected columns, and model identity can become less straightforward than people expect.

Be careful with ordering. Combining querysets does not automatically produce one globally ordered result unless you design for that explicitly.

Finally, do not use Q objects as if they combine arbitrary querysets. Q objects combine filter conditions inside one queryset; they are not a general-purpose merge tool for unrelated result sets.

Summary

  • Use Q objects or | when you want an OR condition on the same model.
  • Use union(), intersection(), or difference() when you need SQL set operations.
  • Use chain() for different models, but remember that the result is not a queryset.
  • Prefer database-level combination when possible for scale and composability.
  • Think carefully about ordering, compatibility, and memory use before combining results.

Course illustration
Course illustration

All Rights Reserved.