¿Cómo puedo encontrar la unión de dos conjuntos de consultas de Django?

92

Tengo un modelo de Django con dos métodos de administrador personalizados. Cada uno devuelve un subconjunto diferente de los objetos del modelo, según una propiedad diferente del objeto.

¿Hay alguna forma de obtener un conjunto de consultas, o simplemente una lista de objetos, esa es la unión de los conjuntos de consultas devueltos por cada método de administrador?

Paul D. Waite
fuente
3
(De una respuesta eliminada) Consulte esta pregunta para ver una variación que funciona con QuerySets de diferentes modelos: stackoverflow.com/questions/431628/…
rnevius
1
A partir de la versión 1.11, los conjuntos de consultas de django tienen un método de unión incorporado. Lo he agregado como respuesta para referencia futura
Jose Cherian

Respuestas:

179

Esto funciona y se ve un poco más limpio:

records = query1 | query2

Si no desea duplicados, deberá agregar .distinct():

records = (query1 | query2).distinct()
Jordan Reiter
fuente
5
Si bien la respuesta aceptada devuelve una unión iterable (lista para ser exactos), como OP ha pedido, este método devuelve una verdadera unión de conjuntos de consultas. Este conjunto de consultas se puede seguir operando, lo que se desea en muchas circunstancias.
Krystian Cybulski
5
Debido a un error de Django, esta construcción a veces puede devolver resultados incorrectos cuando se trata de ManyToManyFields. Por ejemplo, a veces verá que records.count()será mayor que query1.count() + query2.count(), lo cual es claramente incorrecto.
Jian
4
@Jian, ¿puedes aclarar la versión de django con el error y un enlace al problema de djangoproject?
IMFletcher
10
registros = consulta1 | query2; records = records.distinct () me daría el resultado correcto
eugene
5
Puede sobrecargar operadores en Python. Consulte docs.python.org/2/library/operator.html . Entonces, lo que hace Django es crear métodos especiales para el objeto QuerySet. Vea el código aquí: github.com/django/django/blob/master/django/db/models/… la QuerySetclase proporciona métodos __and__y __or__que se llaman cuando los operadores &o |se usan entre dos QuerySetobjetos (también se usan para la Qclase ).
Jordan Reiter
49

A partir de la versión 1.11 , los conjuntos de consultas de django tienen un método de unión incorporado.

q = q1.union(q2) #q will contain all unique records of q1 + q2
q = q1.union(q2, all=True) #q will contain all records of q1 + q2 including duplicates
q = q1.union(q2,q3) # more than 2 queryset union

Vea la publicación de mi blog sobre esto para obtener más ejemplos.

Jose Cherian
fuente
No pude conseguir que all = True funcionara. Terminé lanzando mi conjunto de consultas a un conjunto antes de devolverlo al cliente.
Braden Holt
1
@BradenHolt, all = True, significa que contendrá registros duplicados. Simplemente puede eliminar all = True para evitar convertirlo en un conjunto.
Jose Cherian
después de que esto no funcione DjangoFilterBackend, ¿cómo puedo usar union y DjangoFilterBackend?
nesalexy
Desafortunadamente, esto no parece funcionar para modelos con un orden predeterminado definido en el Meta del modelo. Siempre que intento combinarlos con .union, recibo el siguiente error: "ORDER BY no está permitido en subconsultas de declaraciones compuestas".
2019
4

Sugeriría usar 'query1.union (query2)' en lugar de 'query1 | query2 '; Obtuve resultados diferentes de los dos métodos anteriores y el primero es lo que esperaba. Lo siguiente es lo que me encontré:

print "union result:"
for element in query_set1.union(query_set2):
    print element

print "| result:"
for element in (query_set1 | query_set2):
    print element

resultado:

union result:
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object

| result:
KafkaTopic object
KafkaTopic object
Xianxing
fuente
1
Pegue el código, no las imágenes del código. El texto de las imágenes no se puede buscar, no puede copiarlo / pegarlo en su editor para su verificación y ocupa más espacio del necesario. Use comillas invertidas para marcar el código como código, para que se formatee correctamente. Vea el enlace de "ayuda" junto al cuadro de entrada de texto.
2019
Gracias por actualizar. :)
jrial