Reputation: 45
I've got a list of Django Querysets that I'd like to combine into a single Query. The standard way of merging two Querysets is to perform the operation: newQ = Q1 | Q2
, and I'd like to perform that operation on all elements of my list to form a single Queryset object.
This is pretty straightforward to do using a for loop, eg:
for qs in qs_list:
if final_qs not in locals():
final_qs = qs
else:
final_qs = final_qs | qs
Given the wonders of python it feels as though there is probably an inbuilt function of some kind that will do this for you. However, I had a look through the itertools
library and nothing jumped out as a way of simplifying this operation.
So my question is, is there a more pythonic way of performing the above operation?
Upvotes: 1
Views: 133
Reputation: 1124988
Yes, that function is called functools.reduce()
. Use it with operator.or_()
:
import operator
from functools import reduce
final_qs = reduce(operator.or_, qs_list)
reduce()
takes the first to values of qs_list
, passes those to the first argument, operator.or_
, effectively executing qs_list[0] | qs_list[1]
. It then takes that result, plus the next value in qs_list
and applies the first argument again, and so on until qs_list
is done.
For qs_list
with 4 elements, that comes down to:
or_(or_(or_(qs_list[0], qs_list[1]), qs_list[2]), qs_list[3])
or the equivalent of:
qs_list[0] | qs_list[1] | qs_list[2] | qs_list[3]
but reduce works for any length of qs_list
> 0 (for a list of length 1 the first value is returned without applying the first argument).
Note that your use of if final_qs not in locals()
is very unpythonic; don't ever do that. You could have written your loop with an initial final_qs
from qs_list[0]
instead:
final_qs = qs_list[0]
for qs in qs_list[1:]:
final_qs |= qs
Upvotes: 5