我试图建立一个我正在建立的Django网站的搜索,在这个搜索中,我用三种不同的模式进行搜索。为了在搜索结果列表上进行分页,我想使用一个通用的object_list视图来显示结果。但要做到这一点,我必须将三个查询集合并为一个。

我该怎么做?我已经试过了:

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_list、article_list和post_list这三个列表?


当前回答

这也可以通过两种方式实现。

第一种方法

对queryset|使用union运算符可以合并两个queryset。如果两个查询集属于同一模型/单个模型,则可以使用联合运算符组合查询集。

对于一个实例

pagelist1 = Page.objects.filter(
    Q(title__icontains=cleaned_search_term) | 
    Q(body__icontains=cleaned_search_term))
pagelist2 = Page.objects.filter(
    Q(title__icontains=cleaned_search_term) | 
    Q(body__icontains=cleaned_search_term))
combined_list = pagelist1 | pagelist2 # this would take union of two querysets

第二种方法

实现两个查询集之间的组合操作的另一种方法是使用itertools链函数。

from itertools import chain
combined_results = list(chain(pagelist1, pagelist2))

其他回答

将查询集连接到列表中是最简单的方法。如果所有查询集都会命中数据库(例如,因为结果需要排序),这不会增加更多成本。

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

您可以使用Union:

qs = qs1.union(qs2, qs3)

但是如果您想对组合查询集的外部模型应用order_by。。。那么你需要事先这样选择它们。。。否则它不会起作用。

实例

qs = qs1.union(qs2.select_related("foreignModel"), qs3.select_related("foreignModel"))
qs.order_by("foreignModel__prop1")

其中prop1是外国模型中的属性。

这也可以通过两种方式实现。

第一种方法

对queryset|使用union运算符可以合并两个queryset。如果两个查询集属于同一模型/单个模型,则可以使用联合运算符组合查询集。

对于一个实例

pagelist1 = Page.objects.filter(
    Q(title__icontains=cleaned_search_term) | 
    Q(body__icontains=cleaned_search_term))
pagelist2 = Page.objects.filter(
    Q(title__icontains=cleaned_search_term) | 
    Q(body__icontains=cleaned_search_term))
combined_list = pagelist1 | pagelist2 # this would take union of two querysets

第二种方法

实现两个查询集之间的组合操作的另一种方法是使用itertools链函数。

from itertools import chain
combined_results = list(chain(pagelist1, pagelist2))

此递归函数将查询集数组连接成一个查询集。

def merge_query(ar):
    if len(ar) ==0:
        return [ar]
    while len(ar)>1:
        tmp=ar[0] | ar[1]
        ar[0]=tmp
        ar.pop(1)
        return ar

相关的,对于混合来自同一模型的查询集,或者对于来自几个模型的类似字段,从Django 1.11开始,也可以使用QuerySet.union()方法:

union()联合(*other_qs,all=False)Django 1.11新增。使用SQL的UNION运算符组合两个或多个QuerySet的结果。例如:>>>qs1.联合(qs2,qs3)默认情况下,UNION运算符仅选择不同的值。要允许重复值,请使用all=True论点union()、intersection()和difference()返回模型实例第一个QuerySet的类型,即使参数是其他型号。只要SELECT列表在所有QuerySet中都是相同的(至少是类型,名称不是只要类型的顺序相同)。此外,只有LIMIT、OFFSET和ORDER BY(即切片和order_by())。此外,数据库对允许在联合查询。例如,大多数数据库不允许LIMIT或OFFSET组合查询。