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


当前回答

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

其他回答

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

试试看:

matches = pages | articles | posts

它保留了查询集的所有功能,如果您想按或类似方式排序,这是很好的。

请注意:这不适用于来自两个不同模型的查询集。

要求:Django==2.0.2,Django querysetsequence==0.8

如果您希望组合查询集,但仍然使用QuerySet,则可能需要检查django查询集序列。

但需要注意的是,它只需要两个查询集作为参数。但使用python-reduce,您可以始终将其应用于多个查询集。

from functools import reduce
from queryset_sequence import QuerySetSequence

combined_queryset = reduce(QuerySetSequence, list_of_queryset)

下面是我遇到的一个情况,以及我如何使用列表理解、reduce和django查询集序列

from functools import reduce
from django.shortcuts import render    
from queryset_sequence import QuerySetSequence

class People(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    mentor = models.ForeignKey('self', null=True, on_delete=models.SET_NULL, related_name='my_mentees')

class Book(models.Model):
    name = models.CharField(max_length=20)
    owner = models.ForeignKey(Student, on_delete=models.CASCADE)

# as a mentor, I want to see all the books owned by all my mentees in one view.
def mentee_books(request):
    template = "my_mentee_books.html"
    mentor = People.objects.get(user=request.user)
    my_mentees = mentor.my_mentees.all() # returns QuerySet of all my mentees
    mentee_books = reduce(QuerySetSequence, [each.book_set.all() for each in my_mentees])

    return render(request, template, {'mentee_books' : mentee_books})

最好的选择是使用Django内置方法:

# Union method
result_list = page_list.union(article_list, post_list)

这将返回这些查询集中所有对象的并集。

如果您只想获取三个查询集中的对象,您会喜欢内置的查询集方法intersection。

# intersection method
result_list = page_list.intersection(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))