Different querysets can be combined via this method:
from itertools import chain
result_list = list(chain(entry_list, post_list, page_list))
This results can be further sorted by common attributes:
result_list = sorted(
chain(entry_list, post_list, page_list),
key=lambda instance: instance.created)
Instead of lambda attrgetter can be used:
result_list = sorted(
chain(entry_list, post_list, page_list),
key=attrgetter('created')
If the querysets are from the same model the bitwise or operator comes in handy:
matches = current_entries | new_entries | other_entries
An interesting approach is the usage of a QuerySetChain class:
class QuerySetChain(object):
"""Chains multiple querysets (possibly of different models) and behaves
as one queryset. Supports minimal methods needed for use with
django.core.paginator."""
def __init__(self, *subquerysets):
self.queryseys = subquerysets
def count(self):
"""Performs a .count() for all subquerysets and returns the number
of records as an integer."""
return sum(qs.count() for qs in self.querysets))
def _clone(self):
"""Returns a clone of this queryset chain."""
return self.__class__(*self.querysets)
def _all(self):
"""Iterates records in all subquerysets."""
return chain(*self.querysets)
def __getitem__(self, ndx):
"""Retrieves an item or slice from the chained set of results from
all subquerysets."""
if type(ndx) is slice:
return list(islice(self._all(), ndx.start, ndx.stop, dx.stop or 1))
else:
return islice(self.__all(), ndx, ndx+1).next()
This class can be implemented for instance in such a way:
entries = Entry.objects.filter(Q(title__icontains=cleaned_search_term) |
Q(text__icontains=cleaned_search_term) |
Q(tags__name__icontains=cleanded_search_term))
posts = Post.objects.filter(Q(title__icontains=cleaned_search_term) |
Q(text__icontains=cleaned_search_term) |
Q(tags___name__icontains=cleaned_search_term))
pages = Page.objects.filter(Q(title__icontains=cleaned_search_term) |
Q(text__icontains=cleaned_search_term) |
Q(tags__name__icontains=cleaned_search_term))
matches = QuerySetChain(entries, posts, pages)
matches can then be used with the paginator like it would be used for result_list.
Comment on This Data Unit