본문 바로가기
Python/Python FAQ

Python Django에서 여러 QuerySet을 어떻게 결합하나요?, How to combine multiple QuerySets in Django?

by 베타코드 2023. 7. 3.
반응형

질문


저는 현재 구축 중인 Django 사이트의 검색 기능을 구축하려고 합니다. 이 검색에서는 세 가지 다른 모델에서 검색을 수행합니다. 그리고 검색 결과 목록에 페이지네이션을 적용하기 위해 일반적인 object_list 뷰를 사용하여 결과를 표시하고 싶습니다. 그러려면 세 개의 QuerySet을 하나로 병합해야 합니다.

어떻게 할 수 있을까요? 다음과 같이 시도해 보았습니다:

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 속성이 없기 때문입니다.

page_list, article_listpost_list를 병합하는 방법은 무엇일까요?


답변


쿼리셋을 리스트로 연결하는 것은 가장 간단한 접근 방법입니다. 데이터베이스가 모든 쿼리셋에 대해 조회될 것이기 때문에 (예: 결과를 정렬해야 하는 경우) 추가적인 비용이 발생하지 않습니다.

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

itertools.chain을 사용하는 것은 각 리스트를 순환하고 하나씩 요소를 추가하는 것보다 빠릅니다. 왜냐하면 itertools는 C로 구현되어 있기 때문입니다. 또한 각 쿼리셋을 리스트로 변환하기 전에 연결하는 것보다 적은 메모리를 사용합니다.

이제 결과 리스트를 날짜별로 정렬할 수 있습니다 (다른 답변에 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,
)
반응형

댓글