Como posso encontrar a união de dois querysets Django?

94

Eu tenho um modelo Django com dois métodos de gerenciamento personalizados. Cada um retorna um subconjunto diferente de objetos do modelo, com base em uma propriedade diferente do objeto.

Existe alguma maneira de obter um queryset, ou apenas uma lista de objetos, que é a união dos querysets retornados por cada método do gerenciador?

Paul D. Waite
fonte
3
(De uma resposta excluída) Veja esta pergunta para uma variação que funciona com QuerySets de modelos diferentes: stackoverflow.com/questions/431628/…
rnevius
1
A partir da versão 1.11, os conjuntos de consultas django têm um método de união integrado. Eu adicionei como uma resposta para referência futura
Jose Cherian

Respostas:

181

Isso funciona e parece um pouco mais limpo:

records = query1 | query2

Se você não quiser duplicatas, precisará acrescentar .distinct():

records = (query1 | query2).distinct()
Jordan Reiter
fonte
6
Enquanto a resposta aceita retorna uma união iterável (lista para ser exato), como OP pediu, este método retorna uma verdadeira união de querysets. Este queryset pode ser operado posteriormente, o que é desejado em muitas circunstâncias.
Krystian Cybulski
5
Devido a um bug do Django, esta construção pode às vezes retornar resultados incorretos ao lidar com ManyToManyFields. Por exemplo, às vezes você verá que records.count()será maior do que query1.count() + query2.count(), o que é claramente incorreto.
Jian
4
@Jian você pode esclarecer a versão do django com o bug e um link para o problema do djangoproject?
IMFletcher
10
registros = consulta1 | query2; records = records.distinct () me daria o resultado correto
eugene
5
Você pode sobrecarregar operadores em Python. Consulte docs.python.org/2/library/operator.html . Então, o que o Django faz é criar métodos especiais para o objeto QuerySet. Veja o código aqui: github.com/django/django/blob/master/django/db/models/… a QuerySetclasse fornece métodos para __and__e __or__que são chamados quando os operadores &ou |são usados ​​entre dois QuerySetobjetos (também usados ​​para a Qclasse também )
Jordan Reiter,
50

A partir da versão 1.11 , django querysets tem um método de união embutido.

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

Veja minha postagem no blog sobre isso para mais exemplos.

Jose Cherian
fonte
Não consegui fazer tudo = True funcionar. Acabei fazendo casting do meu queryset para um conjunto antes de devolvê-lo ao cliente.
Braden Holt
1
@BradenHolt, all = True, significa que conterá registros duplicados. Você pode simplesmente remover all = True para evitar lançá-lo em um conjunto.
Jose Cherian
depois disso não funciona DjangoFilterBackend, como posso usar o Union e o DjangoFilterBackend?
nesalexia de
Infelizmente, isso não parece funcionar para modelos com uma ordem padrão definida no Meta do modelo. Sempre que tento combiná-los com .union, recebo o seguinte erro: "ORDER BY não permitido em subconsultas de instruções compostas."
jrial de
4

Eu sugeriria usar 'query1.union (query2)' em vez de 'query1 | query2 '; Obtive resultados diferentes com os dois métodos acima e o anterior é o que eu esperava. O seguinte é o que eu descobri:

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
fonte
1
Cole o código, não as imagens do código. O texto nas imagens não é pesquisável, você não pode copiar / colar em seu editor para verificação e ocupa mais espaço do que o necessário. Use crases para marcar o código como código, para que seja formatado corretamente. Veja o link "ajuda" próximo à caixa de entrada de texto.
jrial de
Obrigado por atualizar. :)
jrial de