How can I filter a Django query with a list of values?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Django, the standard way to filter by a list of values is the __in lookup. It translates naturally into an SQL IN clause and lets you express "match any of these values" without writing repetitive OR conditions. The main things to get right are empty-list behavior, field type compatibility, and the size of the list you send to the database.
Use __in for a Single Field
The most direct pattern looks like this.
Django builds one query whose condition is equivalent to "genre is in this list." This is almost always the correct starting point.
It Also Works for Primary Keys and Foreign Keys
Filtering by IDs is especially common.
The same pattern works for foreign key fields.
Using the underlying author_id field is often convenient when you already have integer IDs.
Empty Lists Need Intentional Handling
A subtle issue is what should happen when the input list is empty.
Django treats this as a query that matches nothing, which is usually sensible. The important point is to be aware of it. If an empty list should mean "do not filter," then your application logic has to handle that before building the queryset.
That difference is business logic, not an ORM trick.
Use QuerySets or Iterables as Input
The right-hand side of __in does not have to be a literal Python list. It can be any iterable, including another queryset.
This is useful when one query naturally feeds another.
Do Not Replace __in With Manual Q Chains Unnecessarily
Beginners sometimes write many Q expressions joined with | for a simple same-field membership test.
This works, but it is longer and harder to maintain than genre__in=[...]. Save Q objects for cases where different fields or mixed logical conditions are actually needed.
Large Lists Can Become a Database Concern
__in is clean, but a huge list can still be expensive for the database. If you are sending thousands of values, think about:
- whether the field is indexed
- whether chunking is necessary
- whether a join or subquery would express the problem better
For most application-sized lists, __in is fine. The issue only appears when the list grows large enough to change query planning or parameter limits.
Ordering Is Not Preserved Automatically
A queryset filtered with __in does not come back in the same order as the list you passed.
The database chooses its own result order unless you explicitly add order_by(...). If caller order matters, you need extra logic.
Combine With Other Filters Naturally
__in composes well with the rest of the ORM.
This is one reason Django's query API stays readable even as conditions get richer.
Common Pitfalls
- Rewriting a simple membership filter with a long chain of
Qobjects. - Forgetting that an empty list with
__inmatches nothing. - Assuming the result order will match the order of input values.
- Using
__inon a huge unindexed field and then blaming Django for slow SQL. - Passing values whose types do not match the field being filtered.
Summary
- Use
field__in=valueswhen you want to match any value from a list. - The pattern works for normal fields, primary keys, and foreign keys.
- Empty input lists usually produce an empty result set.
- '
__inis simpler than manualQchaining for single-field membership tests.' - For very large lists, think about indexing, chunking, or better query shapes.

