질문
저는 현재 구축 중인 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_list
및 post_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,
)
댓글