Да, там происходит присваивание, как и в цикле for
. Никакая новая область не создается.
Это определенно ожидаемое поведение: в каждом цикле значение привязано к указанному вами имени. Например,
>>> x=0
>>> a=[1,54,4,2,32,234,5234,]
>>> [x for x in a if x>32]
[54, 234, 5234]
>>> x
5234
После того, как это было признано, кажется достаточно легко избежать: не используйте существующие имена для переменных в рамках понятий.
Конкатенация querysets в список является самым простым подходом. Если база данных будет поражена для всего querysets так или иначе (например, потому что результат должен быть отсортирован), это не добавит далее стоимость.
from itertools import chain
result_list = list(chain(page_list, article_list, post_list))
Используя itertools.chain
быстрее, чем цикличное выполнение каждый список и элементы добавления один за другим, так как itertools
реализован в C. Это также использует меньше памяти, чем преобразование каждого queryset в список перед конкатенацией.
Теперь возможно отсортировать получающийся список, например, по дате (согласно просьбе в комментарии hasen j к другому ответу). Эти sorted()
функция удобно принимает генератор и возвращает список:
result_list = sorted(
chain(page_list, article_list, post_list),
key=lambda instance: instance.date_created)
при использовании Python 2.4 или позже можно использовать attrgetter
вместо лямбды. Я не забываю читать об этом являющийся быстрее, но я не видел значимой разности оборотов для миллиона списков объекта.
from operator import attrgetter
result_list = sorted(
chain(page_list, article_list, post_list),
key=attrgetter('date_created'))
Большая оборотная сторона Вашего текущего подхода является своей неэффективностью с большими наборами результата поиска, поскольку у Вас есть к выпадающему весь набор результатов от базы данных каждый раз, даже при том, что Вы только намереваетесь отобразить одну страницу результатов.
, Чтобы к только выпадающему объекты Вам на самом деле нужно от базы данных, необходимо использовать разбиение на страницы на QuerySet, не список. Если Вы делаете это, Django на самом деле нарезает QuerySet, прежде чем запрос будет выполнен, таким образом, SQL-запрос будет использовать СМЕЩЕНИЕ и ОГРАНИЧИВАТЬ, чтобы только получить записи, Вы на самом деле отобразитесь. Но Вы не можете сделать этого, если Вы не можете переполнить свой поиск в единый запрос так или иначе.
, Учитывая, что все три из Ваших моделей имеют заголовок и поля тела, почему бы не использовать образцовое наследование ? Просто имейте все три модели, наследовались от общего предка, который имеет заголовок и тело, и выполните поиск как единый запрос на модели предка.
вот идея... просто выпадающая одна полная страница результатов каждого из трех, и затем выведите 20 наименее полезных..., это устраняет большой querysets и тот способ, которым Вы только жертвуете небольшой производительностью вместо много
Можно использовать QuerySetChain
класс ниже. При использовании его с paginator Django он должен только поразить базу данных COUNT(*)
запросы для всего querysets и SELECT()
запросы только для тех querysets, записи которых отображены на текущей странице.
Примечание, которое необходимо указать template_name=
при использовании QuerySetChain
с универсальными представлениями, даже если цепочечный querysets все использование та же модель.
from itertools import islice, chain
class QuerySetChain(object):
"""
Chains multiple subquerysets (possibly of different models) and behaves as
one queryset. Supports minimal methods needed for use with
django.core.paginator.
"""
def __init__(self, *subquerysets):
self.querysets = subquerysets
def count(self):
"""
Performs a .count() for all subquerysets and returns the number of
records as an integer.
"""
return sum(qs.count() for qs in self.querysets)
def _clone(self):
"Returns a clone of this queryset chain"
return self.__class__(*self.querysets)
def _all(self):
"Iterates records in all subquerysets"
return chain(*self.querysets)
def __getitem__(self, ndx):
"""
Retrieves an item or slice from the chained set of results from all
subquerysets.
"""
if type(ndx) is slice:
return list(islice(self._all(), ndx.start, ndx.stop, ndx.step or 1))
else:
return islice(self._all(), ndx, ndx+1).next()
В Вашем примере, использование было бы:
pages = Page.objects.filter(Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term))
articles = Article.objects.filter(Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term) |
Q(tags__icontains=cleaned_search_term))
posts = Post.objects.filter(Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term) |
Q(tags__icontains=cleaned_search_term))
matches = QuerySetChain(pages, articles, posts)
Затем использование matches
с paginator как Вы использовало result_list
в Вашем примере.
itertools
модуль был представлен в Python 2.3, таким образом, это должно быть доступно во всех версиях Python, Django работает.
Попробуйте это:
matches = pages | articles | posts
Он сохраняет все функции наборов запросов, что хорошо, если вы хотите order_by
или аналогичный.
Обратите внимание: это не работает для наборов запросов из двух разных моделей.