For loop
values = [1, 2, 3]
q = Q(pk__in=[]) # generic "always false" value
for val in values:
q |= Q(pk=val)
Article.objects.filter(q)
Reduce
from functools import reduce
from operator import or_
values = [1, 2, 3]
q_objects = [Q(pk=val) for val in values]
q = reduce(or_, q_objects, Q(pk__in=[]))
Article.objects.filter(q)
Both of these are equivalent to Article.objects.filter(pk__in=values)
Why Q()
is dangerous
It's important to consider what you want when values
is empty. Many answers with Q()
as a starting value will return everything. Q(pk__in=[])
is a better starting value. It's an always-failing Q object that's handled nicely by the optimizer (even for complex equations).
Article.objects.filter(Q(pk__in=[])) # doesn't hit DB
Article.objects.filter(Q(pk=None)) # hits DB and returns nothing
Article.objects.none() # doesn't hit DB
Article.objects.filter(Q()) # returns everything
If you want to return everything when values
is empty, you should AND with ~Q(pk__in=[])
to ensure that behaviour:
values = []
q = Q()
for val in values:
q |= Q(pk=val)
Article.objects.filter(q) # everything
Article.objects.filter(q | author="Tolkien") # only Tolkien
q &= ~Q(pk__in=[])
Article.objects.filter(q) # everything
Article.objects.filter(q | author="Tolkien") # everything
Q()
is nothing, not an always-succeeding Q object. Any operation involving it will just drop it completely.
Article.objects.filter(pk__in=[1, 2, 3])
in modern django, but the question is still relevant if you want to do something a bit more advanced by OR'ing Q objects together. – Discrepant