django
queryset
OR condition
database query
python

How to perform OR condition in django queryset?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In Django, chaining multiple keyword arguments inside one filter() call produces an AND, not an OR. To express OR logic, the normal tool is Q objects, which let you combine conditions explicitly and build more complex query expressions without dropping into raw SQL.

Use Q Objects for OR

Import Q from django.db.models and combine expressions with the bitwise | operator.

python
1from django.db.models import Q
2from books.models import Book
3
4queryset = Book.objects.filter(
5    Q(author="Author1") | Q(title="Django for Everyone")
6)

That query returns books that match either side of the condition. Without Q, two keyword filters would be combined with AND instead.

Group Conditions Deliberately

Q objects become especially useful when the query contains both OR and AND. Parentheses matter because they control how the logic is grouped.

python
1from django.db.models import Q
2from books.models import Book
3
4queryset = Book.objects.filter(
5    (Q(author="Author1") | Q(author="Author2")) &
6    Q(is_published=True)
7)

This means:

  • author is Author1 or Author2
  • and the book must also be published

If you remove the parentheses, the logic changes. Treat Q expressions like boolean algebra, because that is effectively what they are.

Build Dynamic OR Queries

One reason Q objects are so useful is that they make runtime query construction straightforward. Suppose the user can search across title, subtitle, and author with one input string.

python
1from django.db.models import Q
2from books.models import Book
3
4def search_books(term: str):
5    return Book.objects.filter(
6        Q(title__icontains=term) |
7        Q(subtitle__icontains=term) |
8        Q(author__icontains=term)
9    )

This is cleaner than writing raw SQL and still gives Django a normal queryset it can further filter, order, paginate, or combine.

That composability is a major reason to prefer ORM expressions here. The result stays inside Django's query system instead of becoming a special-case string query you have to maintain separately.

Combine Q Objects Incrementally

You do not need to write the whole expression in one line. Incremental construction is often clearer when the conditions are optional.

python
1from django.db.models import Q
2from books.models import Book
3
4def build_query(term=None, category=None):
5    condition = Q()
6
7    if term:
8        condition &= (
9            Q(title__icontains=term) |
10            Q(author__icontains=term)
11        )
12
13    if category:
14        condition &= Q(category__slug=category)
15
16    return Book.objects.filter(condition)

Starting with Q() lets you build up the final expression step by step while preserving readable logic.

Watch for Duplicate Rows After Joins

When the queryset crosses relations, OR conditions can produce duplicate rows because the SQL joins may match multiple related records. In those cases, distinct() is often the missing piece.

python
1from django.db.models import Q
2from books.models import Book
3
4queryset = Book.objects.filter(
5    Q(tags__name="python") | Q(tags__name="django")
6).distinct()

If you forget distinct(), the query may look correct but return the same Book more than once.

Common Pitfalls

The most common mistake is expecting multiple keyword filters to mean OR. In Django, filter(author="A", title="B") always means both conditions must be true.

Another issue is forgetting parentheses when mixing | and &. The expression may still run, but it may encode different logic than you intended.

Developers also sometimes use raw SQL too early. Q objects cover a large amount of normal boolean query logic and keep the result as a queryset that still works with the rest of the ORM.

Finally, be careful when traversing many-to-many or reverse foreign-key relations. OR logic across joins can create duplicates, and distinct() may be necessary to get the semantic result you expected.

Summary

  • Use Q objects to express OR conditions in Django querysets.
  • Combine them with | for OR and & for AND.
  • Add parentheses to make mixed boolean logic explicit.
  • Build Q expressions incrementally when the query depends on runtime input.
  • Use distinct() when relation joins cause duplicate rows.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.