from django.db.models import Q def make_q(operator, criteria): """ Concatenates Q objects for criteria, which is a list of dicts. This centralizes this goofy awkwardness of handling the first item differently than the remaining items. >>> print make_q(operator.or_, [{'x':1, 'y':2}, {'y': 3}]) (OR: ('y', 3), (AND: ('y', 2), ('x', 1))) """ criteria = list(criteria) # copy # Errors should never pass silently. #... #In the face of ambiguity, refuse the temptation to guess. if not criteria: raise ValueError("Refusing to make an empty Q object (no criteria)") q_ = Q(**criteria.pop()) for criterion in criteria: q_ = operator(q_, Q(**criterion)) return q_