如何在 Django 中组合多个 QuerySet?
我正在尝试为我正在构建的 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
?
I'm trying to build the search for a Django site I am building, and in that search, I am searching across three 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 three 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.
How can I merge the three lists, page_list
, article_list
and post_list
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
将查询集连接到列表中是最简单的方法。 如果数据库将被命中所有查询集(例如,因为结果需要排序),这不会增加进一步的成本。
使用
itertools.chain
比循环每个列表并逐个附加元素更快,因为itertools
是用 C 实现的。它比将每个查询集转换为列表消耗的内存更少在连接之前。现在可以对结果列表进行排序,例如按日期(按照 hasen j 对另一个答案的评论中的要求)。
sorted()
函数可以方便地接受生成器并返回一个列表:您可以反转排序顺序:
attrgetter
相当于以下lambda
(这是 Python 2.4 之前必须采用的方式):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.
Using
itertools.chain
is faster than looping each list and appending elements one by one, sinceitertools
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:You can reverse the sort order:
attrgetter
is equivalet to the followinglambda
(this was the way it had to be done before Python 2.4):试试这个:
它保留了查询集的所有功能,如果您想要
order_by
或类似的功能,这会很好。请注意:这不适用于来自两个不同模型的查询集。
Try this:
It retains all the functions of the querysets which is nice if you want to
order_by
or similar.Please note: this doesn't work on querysets from two different models.
相关,用于混合来自同一模型的查询集,或来自几个模型的类似字段,从Django 1.11开始 a
QuerySet.union()
方法 是也提供:Related, for mixing querysets from the same model, or for similar fields from a few models, starting with Django 1.11 a
QuerySet.union()
method is also available:您可以使用下面的
QuerySetChain
类。 当它与 Django 的分页器一起使用时,它应该只对所有查询集使用COUNT(*)
查询来访问数据库,并且只对那些记录显示在的查询集使用SELECT()
查询来访问数据库。当前页面。请注意,如果将
QuerySetChain
与通用视图一起使用,您需要指定template_name=
,即使链接的查询集都使用相同的模型。在您的示例中,用法是:
然后将
matches
与分页器一起使用,就像您在示例中使用result_list
一样。itertools 模块是在 Python 2.3 中引入的,因此它应该在 Django 运行的所有 Python 版本中可用。
You can use the
QuerySetChain
class below. When using it with Django's paginator, it should only hit the database withCOUNT(*)
queries for all querysets andSELECT()
queries only for those querysets whose records are displayed on the current page.Note that you need to specify
template_name=
if using aQuerySetChain
with generic views, even if the chained querysets all use the same model.In your example, the usage would be:
Then use
matches
with the paginator like you usedresult_list
in your example.The
itertools
module was introduced in Python 2.3, so it should be available in all Python versions Django runs on.如果您想链接大量查询集,请尝试以下操作:
其中:docs 是查询集列表
In case you want to chain a lot of querysets, try this:
where: docs is a list of querysets
当前方法的一大缺点是它在处理大型搜索结果集时效率低下,因为您每次都必须从数据库中提取整个结果集,即使您只想显示一页结果。
为了仅从数据库中提取您实际需要的对象,您必须在查询集(而不是列表)上使用分页。 如果这样做,Django 实际上会在执行查询之前对 QuerySet 进行切片,因此 SQL 查询将使用 OFFSET 和 LIMIT 来仅获取您将实际显示的记录。 但除非您能以某种方式将搜索塞入单个查询中,否则您无法做到这一点。
鉴于所有三个模型都有标题和正文字段,为什么不使用 模型继承? 只需让所有三个模型继承具有标题和正文的共同祖先,并在祖先模型上将搜索作为单个查询执行即可。
The big downside of your current approach is its inefficiency with large search result sets, as you have to pull down the entire result set from the database each time, even though you only intend to display one page of results.
In order to only pull down the objects you actually need from the database, you have to use pagination on a QuerySet, not a list. If you do this, Django actually slices the QuerySet before the query is executed, so the SQL query will use OFFSET and LIMIT to only get the records you will actually display. But you can't do this unless you can cram your search into a single query somehow.
Given that all three of your models have title and body fields, why not use model inheritance? Just have all three models inherit from a common ancestor that has title and body, and perform the search as a single query on the ancestor model.
这也可以通过两种方式来实现。
第一种方法
使用 QuerySet
|
的联合运算符来合并两个查询集。 如果两个 QuerySet 属于同一模型/单个模型,则可以使用 union 运算符组合 QuerySet。举个例子
第二种方法
实现两个查询集之间的组合操作的另一种方法是使用itertools链函数。
This can be achieved by two ways either.
1st way to do this
Use union operator for QuerySet
|
to take union of two QuerySets. If both QuerySets belongs to the same model / a single model then it is possible to combine QuerySets by using the union operator.For an instance
2nd way to do this
One other way to achieve combine operations between two QuerySets is to use the itertools chain function.
您可以使用Union:
但是如果您想应用< code>order_by 在组合查询集的外部模型上......那么你需要 以这种方式预先选择它们...否则它将不起作用。
示例
,其中
prop1
是外部模型中的属性。You can use Union:
But if you want to apply
order_by
on the foreign models of the combined queryset... then you need to Select them beforehand this way... otherwise it won't work.Example
where
prop1
is a property in the foreign model.引用自 https://groups.google.com/forum/#!topic/django-users/ 6wUNuJa4jVw。 请参阅亚历克斯·盖纳
Quoted from https://groups.google.com/forum/#!topic/django-users/6wUNuJa4jVw. See Alex Gaynor
要求:
Django==2.0.2
、django-querysetsequence==0.8
如果您想组合
querysets
并且仍然得到QuerySet
,您可能需要查看 django-queryset-sequence。但有一点需要注意。 它只需要两个
查询集
作为参数。 但使用 pythonreduce
,您始终可以将其应用于多个queryset
。就是这样。 下面是我遇到的情况以及我如何使用
列表理解
、reduce
和django-queryset-sequence
Requirements:
Django==2.0.2
,django-querysetsequence==0.8
In case you want to combine
querysets
and still come out with aQuerySet
, you might want to check out django-queryset-sequence.But one note about it. It only takes two
querysets
as it's argument. But with pythonreduce
you can always apply it to multiplequeryset
s.And that's it. Below is a situation I ran into and how I employed
list comprehension
,reduce
anddjango-queryset-sequence
这里有一个想法...只需从这三个结果中的每一个中提取一整页结果,然后扔掉 20 个最无用的结果...这消除了大型查询集,这样您只牺牲了一点性能,而不是牺牲了很多。
Here's an idea... just pull down one full page of results from each of the three and then throw out the 20 least useful ones... this eliminates the large querysets and that way you only sacrifice a little performance instead of a lot.
最好的选择是使用 Django 内置方法:
这将返回这些查询集中所有对象的并集。
如果您只想获取三个查询集中的对象,您会喜欢查询集的内置方法,
intersection
。The best option is to use the Django built-in methods:
That will return the union of all the objects in those querysets.
If you want to get just the objects that are in the three querysets, you will love the built-in method of querysets,
intersection
.这将在不使用任何其他库的情况下完成工作:
This will do the work without using any other libraries:
您可以使用 "|"(按位或) 组合相同的查询集模型如下:
控制台输出:
并且,您可以使用
|=
添加相同模型的查询集,如下所示:控制台输出:
如果添加不同模型的查询集,请小心如下所示:
下面有一个错误:
但是,如果添加不同模型的空查询集,如下所示:
下面没有错误:
再次小心,如果通过 get() 如下所示:
有错误以下:
You can use "|"(bitwise or) to combine the querysets of the same model as shown below:
Output on console:
And, you can use
|=
to add the queryset of the same model as shown below:Output on console:
Be careful, if adding the queryset of a different model as shown below:
There is an error below:
But, if adding the empty queryset of a different model as shown below:
There is no error below:
Again be careful, if adding the object by get() as shown below:
There is an error below:
要获取两个查询集的交集:
To get the intersection of both querysets:
此递归函数将一组查询集连接成一个查询集。
This recursive function concatenates array of querysets into one queryset.