1. ホーム
  2. django

[解決済み] Django のビューで 2 つ以上のクエリセットを結合するにはどうすればよいですか?

2022-03-22 20:48:48

質問

私が構築している Django サイトの検索を構築しようとしているのですが、その検索では、3つの異なるモデルで検索しているのです。そして、検索結果リストのページネーションを得るために、汎用的な object_list ビューを使って結果を表示したいのです。しかし、それを行うには、3つのクエリセットを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")

しかし、これではうまくいかない。そのリストを汎用ビューで使おうとすると、エラーが発生します。このリストにはclone属性がありません。

3つのリストを統合するにはどうすればよいですか。 page_list , article_listpost_list ?

解決方法は?

クエリセットをリストに結合するのは、最もシンプルな方法です。もしデータベースがすべてのクエリセットに対してヒットするのであれば(例えば、結果をソートする必要があるため)、これはさらなるコストを増加させないでしょう。

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

使用方法 itertools.chain は、各リストをループして要素を1つずつ追加するよりも高速です。 itertools また、各クエリセットをリストに変換してから連結するよりも少ないメモリ消費量で済みます。

これで、結果のリストを例えば日付でソートすることが可能になりました(別の回答に対するhasen jさんのコメントで要望がありました)。その sorted() 関数は、ジェネレータを受け取り、リストを返すという便利なものです。

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

Python 2.4以降を使用している場合は、Python 2.4以降を使用している場合は、以下のように attrgetter ラムダの代わりに 高速化されるという記事を読んだ記憶がありますが、100万アイテムのリストでは目立った速度差は感じられませんでした。

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