Combining Querysets

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),

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

    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))
            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) |
posts = Post.objects.filter(Q(title__icontains=cleaned_search_term) |
                            Q(text__icontains=cleaned_search_term) |
pages = Page.objects.filter(Q(title__icontains=cleaned_search_term) |
                            Q(text__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.

Edit tutorial

Comment on This Data Unit