Estoy tratando de construir la búsqueda de un sitio Django que estoy construyendo, y en esa búsqueda, estoy buscando en 3 modelos diferentes. Y para obtener la paginación en la lista de resultados de búsqueda, me gustaría usar una vista genérica de lista de objetos para mostrar los resultados. Pero para hacer eso, tengo que fusionar 3 conjuntos de consultas en uno.
¿Cómo puedo hacer eso? He intentado esto:
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")
Pero esto no funciona. Recibo un error cuando intento usar esa lista en la vista genérica. A la lista le falta el atributo de clonación.
¿Alguien sabe cómo puedo fusionar las tres listas page_list
, article_list
y post_list
?
django
search
django-queryset
django-q
espenhogbakk
fuente
fuente
union
.Respuestas:
Concatenar los conjuntos de consultas en una lista es el enfoque más simple. Si la base de datos se verá afectada por todos los conjuntos de consultas de todos modos (por ejemplo, porque el resultado necesita ser ordenado), esto no agregará más costos.
Usar
itertools.chain
es más rápido que recorrer cada lista y agregar elementos uno por uno, ya queitertools
se implementa en C. También consume menos memoria que convertir cada conjunto de consultas en una lista antes de concatenar.Ahora es posible ordenar la lista resultante, por ejemplo, por fecha (como se solicitó en el comentario de Hasen J a otra respuesta). La
sorted()
función acepta convenientemente un generador y devuelve una lista:Si está utilizando Python 2.4 o posterior, puede usar en
attrgetter
lugar de una lambda. Recuerdo haber leído que era más rápido, pero no vi una diferencia de velocidad notable para una lista de millones de artículos.fuente
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'
Prueba esto:
Conserva todas las funciones de los conjuntos de consultas, lo cual es bueno si lo desea
order_by
o similar.Tenga en cuenta: esto no funciona en conjuntos de consultas de dos modelos diferentes.
fuente
|
está el operador de unión de conjuntos, no bit a bit O.Relacionado, para mezclar conjuntos de consultas del mismo modelo, o para campos similares de algunos modelos, a partir de Django 1.11 también está disponible un
qs.union()
método :https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.union
fuente
Puedes usar la
QuerySetChain
clase a continuación. Cuando se usa con el paginador de Django, solo debe llegar a la base de datos conCOUNT(*)
consultas para todos los conjuntos deSELECT()
consultas y consultas solo para aquellos conjuntos de consultas cuyos registros se muestran en la página actual.Tenga en cuenta que debe especificar
template_name=
si usa unQuerySetChain
con vistas genéricas, incluso si los conjuntos de consultas encadenados usan el mismo modelo.En su ejemplo, el uso sería:
Luego utilícelo
matches
con el paginador como usóresult_list
en su ejemplo.El
itertools
módulo se introdujo en Python 2.3, por lo que debería estar disponible en todas las versiones de Python en las que se ejecuta Django.fuente
La gran desventaja de su enfoque actual es su ineficiencia con grandes conjuntos de resultados de búsqueda, ya que debe extraer todo el conjunto de resultados de la base de datos cada vez, aunque solo tenga la intención de mostrar una página de resultados.
Para extraer solo los objetos que realmente necesita de la base de datos, debe usar la paginación en un QuerySet, no en una lista. Si hace esto, Django en realidad corta el QuerySet antes de que se ejecute la consulta, por lo que la consulta SQL usará OFFSET y LIMIT para obtener solo los registros que realmente mostrará. Pero no puede hacer esto a menos que pueda agrupar su búsqueda en una sola consulta de alguna manera.
Dado que los tres modelos tienen campos de título y cuerpo, ¿por qué no usar la herencia del modelo ? Simplemente haga que los tres modelos hereden de un ancestro común que tenga título y cuerpo, y realice la búsqueda como una sola consulta en el modelo de ancestro.
fuente
En caso de que desee encadenar muchos conjuntos de consultas, intente esto:
donde: docs es una lista de conjuntos de consultas
fuente
Citado de https://groups.google.com/forum/#!topic/django-users/6wUNuJa4jVw . Ver a Alex Gaynor
fuente
Esto se puede lograr de dos maneras.
Primera forma de hacer esto
Utilice el operador de unión para el conjunto de consultas
|
para tomar la unión de dos conjuntos de consultas. Si ambos conjuntos de consultas pertenecen al mismo modelo / modelo único, es posible combinar conjuntos de consultas utilizando el operador de unión.Por una instancia
2da forma de hacer esto
Otra forma de lograr la operación combinada entre dos conjuntos de consultas es usar la función de cadena itertools .
fuente
Requisitos:
Django==2.0.2
,django-querysetsequence==0.8
En caso de que desee combinar
querysets
y aún así salir con unQuerySet
, es posible que desee consultar django-queryset-secuencia .Pero una nota al respecto. Solo se necesitan dos
querysets
como argumento. Pero con pythonreduce
siempre puedes aplicarlo a múltiplesqueryset
s.Y eso es. A continuación hay una situación en la que me encontré y cómo empleé
list comprehension
,reduce
ydjango-queryset-sequence
fuente
Book.objects.filter(owner__mentor=mentor)
No hace lo mismo? No estoy seguro de que este sea un caso de uso válido. Creo que esBook
posible que deba tener varios correosowner
electrónicos antes de que necesite comenzar a hacer algo así.He aquí una idea ... simplemente despliegue una página completa de resultados de cada uno de los tres y luego arroje los 20 menos útiles ... esto elimina los grandes conjuntos de consultas y de esa manera solo sacrifica un poco de rendimiento en lugar de mucho
fuente
Esto hará el trabajo sin usar ninguna otra biblioteca
fuente
Esta función recursiva concatena una matriz de conjuntos de consultas en un conjunto de consultas.
fuente