Estou tentando criar a pesquisa para um site do Django que estou construindo e nessa pesquisa estou pesquisando em 3 modelos diferentes. E para obter a paginação na lista de resultados da pesquisa, eu gostaria de usar uma visualização genérica de object_list para exibir os resultados. Mas para fazer isso, eu tenho que mesclar 3 conjuntos de consultas em um.
Como eu posso fazer isso? Eu tentei isso:
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")
Mas isso não funciona. Eu recebo um erro quando tento usar essa lista na exibição genérica. A lista está ausente do atributo clone.
Alguém sabe como eu posso mesclar as três listas page_list
, article_list
e post_list
?
django
search
django-queryset
django-q
espenhogbakk
fonte
fonte
union
.Respostas:
Concatenar os conjuntos de consultas em uma lista é a abordagem mais simples. Se o banco de dados for atingido para todos os conjuntos de consultas de qualquer maneira (por exemplo, porque o resultado precisa ser classificado), isso não aumentará o custo.
O uso
itertools.chain
é mais rápido do que repetir cada lista e acrescentar elementos um a um, uma vez queitertools
é implementado em C. Ele também consome menos memória do que converter cada conjunto de consultas em uma lista antes de concatenar.Agora é possível classificar a lista resultante, por exemplo, por data (conforme solicitado no comentário de hasen j para outra resposta). A
sorted()
função aceita convenientemente um gerador e retorna uma lista:Se você estiver usando o Python 2.4 ou posterior, poderá usar em
attrgetter
vez de um lambda. Lembro-me de ler sobre ser mais rápido, mas não vi uma diferença de velocidade perceptível para um milhão de itens.fonte
from itertools import groupby
unique_results = [rows.next() for (key, rows) in groupby(result_list, key=lambda obj: obj.id)]
'list' object has no attribute 'complex_filter'
Tente o seguinte:
Ele mantém todas as funções dos conjuntos de consultas, o que é bom se você quiser
order_by
ou algo semelhante.Observe: isso não funciona em conjuntos de consultas de dois modelos diferentes.
fonte
|
está o operador de união de conjunto, não OR bit a bit.Relacionado, para misturar conjuntos de consultas do mesmo modelo ou para campos semelhantes de alguns modelos, a partir do Django 1.11
qs.union()
, também está disponível um método :https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.union
fonte
Você pode usar a
QuerySetChain
classe abaixo. Ao usá-lo com o paginador do Django, ele só deve atingir o banco de dados comCOUNT(*)
consultas para todos os conjuntos deSELECT()
consultas e consultas apenas para os conjuntos de consultas cujos registros são exibidos na página atual.Observe que você precisa especificar
template_name=
se está usando aQuerySetChain
com visualizações genéricas, mesmo que todos os conjuntos de consultas em cadeia usem o mesmo modelo.No seu exemplo, o uso seria:
Em seguida, use
matches
com o paginador como você usouresult_list
no seu exemplo.O
itertools
módulo foi introduzido no Python 2.3, portanto deve estar disponível em todas as versões do Python nas quais o Django roda.fonte
A grande desvantagem de sua abordagem atual é sua ineficiência com grandes conjuntos de resultados de pesquisa, pois você precisa extrair todo o conjunto de resultados do banco de dados a cada vez, mesmo que apenas pretenda exibir uma página de resultados.
Para retirar apenas os objetos que você realmente precisa do banco de dados, você deve usar a paginação em um QuerySet, não em uma lista. Se você fizer isso, o Django fatiará o QuerySet antes da execução da consulta, portanto a consulta SQL usará OFFSET e LIMIT para obter apenas os registros que você realmente exibirá. Mas você não pode fazer isso, a menos que possa agrupar sua pesquisa em uma única consulta de alguma forma.
Como todos os três modelos têm campos de título e corpo, por que não usar a herança de modelos ? Apenas tenha todos os três modelos herdados de um ancestral comum que possua título e corpo e execute a pesquisa como uma única consulta no modelo de ancestral.
fonte
Caso você queira encadear muitos conjuntos de consultas, tente o seguinte:
onde: docs é uma lista de conjuntos de consultas
fonte
Citado em https://groups.google.com/forum/#!topic/django-users/6wUNuJa4jVw . Veja Alex Gaynor
fonte
Isso também pode ser alcançado de duas maneiras.
1ª maneira de fazer isso
Use o operador union para o queryset
|
para obter a união de dois queryset. Se o conjunto de consultas pertencer ao mesmo modelo / modelo único, será possível combinar conjuntos de consultas usando o operador union.Para uma instância
Segunda maneira de fazer isso
Uma outra maneira de obter uma operação combinada entre dois conjuntos de consultas é usar a função de cadeia itertools .
fonte
Requisitos:
Django==2.0.2
,django-querysetsequence==0.8
Caso você deseje combinar
querysets
e ainda obter um aQuerySet
, convém verificar django-queryset-sequence .Mas uma nota sobre isso. São necessários apenas dois
querysets
como argumento. Mas com pythonreduce
você sempre pode aplicá-lo a váriosqueryset
s.E é isso. Abaixo está uma situação em que me deparei e como eu empregava
list comprehension
,reduce
edjango-queryset-sequence
fonte
Book.objects.filter(owner__mentor=mentor)
faz a mesma coisa? Não tenho certeza se este é um caso de uso válido. Eu acho que umBook
pode precisar ter váriosowner
s antes de você começar a fazer algo assim.aqui está uma idéia ... basta puxar uma página inteira de resultados de cada um dos três e depois jogar fora os 20 menos úteis ... isso elimina os grandes conjuntos de consultas e, dessa forma, você sacrifica apenas um pouco de desempenho em vez de muito
fonte
Isso fará o trabalho sem usar outras bibliotecas
fonte
Essa função recursiva concatena a matriz de conjuntos de consultas em um único conjunto de consultas.
fonte