¿Cómo combinar múltiples QuerySets en Django?

Resuelto espenhogbakk asked hace 15 años • 16 respuestas

Estoy intentando crear la búsqueda para un sitio Django que estoy creando y, en esa búsqueda, estoy buscando en tres modelos diferentes. Y para obtener 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 tres QuerySets en uno.

¿Cómo puedo hacer eso? He probado 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.

¿Cómo puedo fusionar las tres listas page_list, article_listy post_list?

espenhogbakk avatar Jan 11 '09 02:01 espenhogbakk
Aceptado

Concatenar los conjuntos de consultas en una lista es el enfoque más sencillo. Si la base de datos se verá afectada por todos los conjuntos de consultas de todos modos (por ejemplo, porque es necesario ordenar el resultado), esto no agregará costos adicionales.

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

Usar itertools.chaines más rápido que hacer un bucle en cada lista y agregar elementos uno por uno, ya que itertoolsestá implementado 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 solicita en el comentario de hasen j en otra respuesta). La sorted()función acepta convenientemente un generador y devuelve una lista:

from operator import attrgetter
result_list = sorted(
    chain(page_list, article_list, post_list),
    key=attrgetter('date_created')
)

Puede invertir el orden de clasificación:

result_list = sorted(
    chain(page_list, article_list, post_list),
    key=attrgetter('date_created'),
    reverse=True,
)

attrgetteres equivalente a lo siguiente lambda(así era como debía hacerse antes de Python 2.4):

result_list = sorted(
    chain(page_list, article_list, post_list),
    key=lambda instance: instance.date_created,
)
akaihola avatar Jan 12 '2009 08:01 akaihola

Prueba esto:

matches = pages | articles | posts

Conserva todas las funciones de los conjuntos de consultas, lo cual es bueno si lo desea order_byo similar.

Tenga en cuenta: esto no funciona en conjuntos de consultas de dos modelos diferentes.

 avatar Apr 28 '2009 05:04