How to combine two or more querysets in a Django view?

后端 未结 13 2413
猫巷女王i
猫巷女王i 2020-11-21 22:40

I am trying to build the search for a Django site I am building, and in that search, I am searching in 3 different models. And to get pagination on the search result list, I

13条回答
  •  既然无缘
    2020-11-21 23:32

    You can use the QuerySetChain class below. When using it with Django's paginator, it should only hit the database with COUNT(*) queries for all querysets and SELECT() queries only for those querysets whose records are displayed on the current page.

    Note that you need to specify template_name= if using a QuerySetChain with generic views, even if the chained querysets all use the same model.

    from itertools import islice, chain
    
    class QuerySetChain(object):
        """
        Chains multiple subquerysets (possibly of different models) and behaves as
        one queryset.  Supports minimal methods needed for use with
        django.core.paginator.
        """
    
        def __init__(self, *subquerysets):
            self.querysets = 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, ndx.step or 1))
            else:
                return islice(self._all(), ndx, ndx+1).next()
    

    In your example, the usage would be:

    pages = Page.objects.filter(Q(title__icontains=cleaned_search_term) |
                                Q(body__icontains=cleaned_search_term))
    articles = Article.objects.filter(Q(title__icontains=cleaned_search_term) |
                                      Q(body__icontains=cleaned_search_term) |
                                      Q(tags__icontains=cleaned_search_term))
    posts = Post.objects.filter(Q(title__icontains=cleaned_search_term) |
                                Q(body__icontains=cleaned_search_term) | 
                                Q(tags__icontains=cleaned_search_term))
    matches = QuerySetChain(pages, articles, posts)
    

    Then use matches with the paginator like you used result_list in your example.

    The itertools module was introduced in Python 2.3, so it should be available in all Python versions Django runs on.

提交回复
热议问题