Django で複数のクエリセットを組み合わせるにはどうすればいいですか? 質問する

Django で複数のクエリセットを組み合わせるにはどうすればいいですか? 質問する

構築中の Django サイトの検索を構築しようとしていますが、その検索では 3 つの異なるモデルを検索しています。検索結果リストにページ区切りを表示するには、汎用の object_list ビューを使用して結果を表示したいと思います。ただし、そのためには 3 つの QuerySet を 1 つにマージする必要があります。

どうすればいいでしょうか? 私はこれを試しました:

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_listの3 つのリストを結合するにはどうすればよいでしょうかpost_list?

ベストアンサー1

クエリセットをリストに連結するのが最も簡単な方法です。いずれにしてもすべてのクエリセットがデータベースにヒットする場合 (たとえば、結果をソートする必要があるため)、これによってコストがさらに追加されることはありません。

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

は C で実装されているため、を使用するitertools.chainと、各リストをループして要素を 1 つずつ追加するよりも高速になりますitertools。また、連結する前に各クエリセットをリストに変換するよりもメモリの消費量が少なくなります。

これで、結果のリストを日付などで並べ替えることが可能になりました (別の回答に対する hasen j のコメントで要求されているとおり)。このsorted()関数はジェネレーターを便利に受け入れ、リストを返します。

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

並べ替え順序を逆にすることもできます:

result_list = sorted(
    chain(page_list, article_list, post_list),
    key=attrgetter('date_created'),
    reverse=True,
)

attrgetterこれは次のものと同等ですlambda(これは Python 2.4 より前では実行する必要があった方法です)。

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

おすすめ記事