我试图建立一个我正在建立的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这三个列表?


当前回答

这将在不使用任何其他库的情况下完成工作:

result_list = page_list | article_list | post_list

其他回答

您可以使用“|”(按位或)组合同一模型的查询集,如下所示:

# "store/views.py"

from .models import Food
from django.http import HttpResponse
                                                
def test(request):
                                             # ↓ Bitwise or
    result = Food.objects.filter(name='Apple') | Food.objects.filter(name='Orange')
    print(result)
    return HttpResponse("Test")

控制台上的输出:

<QuerySet [<Food: Apple>, <Food: Orange>]>
[22/Jan/2023 12:51:44] "GET /store/test/ HTTP/1.1" 200 9

并且,可以使用|=添加同一模型的查询集,如下所示:

# "store/views.py"

from .models import Food
from django.http import HttpResponse
                                                
def test(request):
    result = Food.objects.filter(name='Apple')
         # ↓↓ Here
    result |= Food.objects.filter(name='Orange')
    print(result)
    return HttpResponse("Test")

控制台上的输出:

<QuerySet [<Food: Apple>, <Food: Orange>]>
[22/Jan/2023 12:51:44] "GET /store/test/ HTTP/1.1" 200 9

如果添加不同模型的查询集,请小心,如下所示:

# "store/views.py"

from .models import Food, Drink
from django.http import HttpResponse
                                                
def test(request):
          # "Food" model                      # "Drink" model
    result = Food.objects.filter(name='Apple') | Drink.objects.filter(name='Milk')
    print(result)
    return HttpResponse("Test")

下面有一个错误:

AssertionError: Cannot combine queries on two different base models.
[22/Jan/2023 13:40:54] "GET /store/test/ HTTP/1.1" 500 96025

但是,如果添加不同模型的空查询集,如下所示:

# "store/views.py"

from .models import Food, Drink
from django.http import HttpResponse
                                                
def test(request):
          # "Food" model                       # Empty queryset of "Drink" model 
    result = Food.objects.filter(name='Apple') | Drink.objects.none()
    print(result)
    return HttpResponse("Test")

下面没有错误:

<QuerySet [<Food: Apple>]>
[22/Jan/2023 13:51:09] "GET /store/test/ HTTP/1.1" 200 9

再次小心,如果通过get()添加对象,如下所示:

# "store/views.py"

from .models import Food
from django.http import HttpResponse
                                                
def test(request):
    result = Food.objects.filter(name='Apple')
                         # ↓↓ Object
    result |= Food.objects.get(name='Orange')
    print(result)
    return HttpResponse("Test")

下面有一个错误:

AttributeError: 'Food' object has no attribute '_known_related_objects'
[22/Jan/2023 13:55:57] "GET /store/test/ HTTP/1.1" 500 95748

相关的,对于混合来自同一模型的查询集,或者对于来自几个模型的类似字段,从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组合查询。

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

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
DATE_FIELD_MAPPING = {
    Model1: 'date',
    Model2: 'pubdate',
}

def my_key_func(obj):
    return getattr(obj, DATE_FIELD_MAPPING[type(obj)])

And then sorted(chain(Model1.objects.all(), Model2.objects.all()), key=my_key_func)

引用自https://groups.google.com/forum/#!主题/django用户/6wUNuJa4jVw。见Alex Gaynor

如果要链接大量查询集,请尝试以下操作:

from itertools import chain
result = list(chain(*docs))

其中:docs是查询集的列表