如何在Django视图中合并两个或多个查询集? [英] How to combine two or more querysets in a Django view?

查看:555
本文介绍了如何在Django视图中合并两个或多个查询集?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为正在构建的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'))

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 would like to use a generic object_list view to display the results. But to do that, I have to merge 3 querysets into one.

How can I do that? I've tried this:

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

But this doesn't work. I get an error when I try to use that list in the generic view. The list is missing the clone attribute.

Does anybody know how I can merge the three lists, page_list, article_list and post_list?

解决方案

Concatenating the querysets into a list is the simplest approach. If the database will be hit for all querysets anyway (e.g. because the result needs to be sorted), this won't add further cost.

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

Using itertools.chain is faster than looping each list and appending elements one by one, since itertools is implemented in C. It also consumes less memory than converting each queryset into a list before concatenating.

Now it's possible to sort the resulting list e.g. by date (as requested in hasen j's comment to another answer). The sorted() function conveniently accepts a generator and returns a list:

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

If you're using Python 2.4 or later, you can use attrgetter instead of a lambda. I remember reading about it being faster, but I didn't see a noticeable speed difference for a million item list.

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

这篇关于如何在Django视图中合并两个或多个查询集?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆