python – 如何在Django视图中组合2个或更多的查询集?

我正在尝试构建搜索我正在构建的Django网站,并且在搜索中我搜索了3种不同的模型。为了在搜索结果列表中获得分页,我想使用通用的object_list视图来显示结果。但为了做到这一点,我必须将3个查询集合并成一个。

我怎样才能做到这一点?我试过这个:

result_list = []            
page_list = Page.objects.filter(Q(title__icontains=cleaned_search_term) | Q(body__icontains=cleaned_search_term))
article_list = Article.objects.filter(Q(title__icontains=cleaned_search_term) | Q(body__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term))
post_list = Post.objects.filter(Q(title__icontains=cleaned_search_term) | Q(body__icontains=cleaned_search_term) | Q(tags__icontains=cleaned_search_term))

for x in page_list:
    result_list.append(x)
for x in article_list:
    result_list.append(x)
for x in post_list:
    result_list.append(x)

return object_list(request, queryset=result_list, template_object_name='result',
                   paginate_by=10, extra_context={'search_term': search_term},
                   template_name="search/result_list.html")

但是这不起作用当我尝试在通用视图中使用该列表时出现错误。该列表缺少克隆属性。

任何人都知道我可以如何合并这三个列表page_listarticle_listpost_list


将查询集连接到列表是最简单的方法。如果数据库无论如何都会针对所有查询集(例如,因为结果需要排序),这不会增加额外的成本。

from itertools import chain
result_list = list(chain(page_list, article_list, post_list))

使用itertools.chain比循环每个列表和逐个追加元素更快,因为itertools它是用C实现的。它比在连接之前将每个查询集转换为列表的内存消耗更少的内存。

现在可以按照日期对结果列表进行排序(如hasen j的评论中对其他答案的要求)。该sorted()函数方便地接受一个生成器并返回一个列表:

result_list = sorted(
    chain(page_list, article_list, post_list),
    key=lambda instance: instance.date_created)

如果您使用Python 2.4或更高版本,则可以使用attrgetter而不是lambda。我记得阅读速度更快,但是我没有看到一百万条产品列表上明显的速度差异。

from operator import attrgetter
result_list = sorted(
    chain(page_list, article_list, post_list),
    key=attrgetter('date_created'))

尝试这个:

matches = pages | articles | posts

保留查询集的所有功能,如果您想order_by或类似的话,这些功能都很好。

糟糕,请注意,这不适用于来自两种不同模型的查询集…


您可以使用QuerySetChain下面的课程。当在Django的paginator中使用它时,它应该只COUNT(*)针对所有SELECT()查询集的查询和仅查询那些记录显示在当前页面上的查询集的查询。

请注意,即使链接的查询集都使用相同的模型,您也需要指定template_name=是否使用QuerySetChain通用视图。

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

在你的例子中,用法是:

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)

然后matches像您result_list在示例中使用的那样使用paginator 。

itertools模块是在Python 2.3中引入的,所以它应该在Django运行的所有Python版本中都可用。

添加评论

友情链接:蝴蝶教程