Django em / não em consulta

100

Estou tentando descobrir como escrever uma consulta do estilo 'não está em' no Django. Por exemplo, a estrutura de consulta em que estou pensando seria assim.

select table1.* 
from table1
where table1.id not in 
(
  select table2.key_to_table1
  from table2 
  where table2.id = some_parm 
)

Como seria a sintaxe do django assumindo modelos chamados table1 e table2?

Turbo
fonte

Respostas:

163
table1.objects.exclude(id__in=
    table2.objects.filter(your_condition).values_list('id', flat=True))

A função de exclusão funciona como a Notoperadora que você pediu. O atributo flat = Truediz à table2consulta para retornar o value_listcomo uma lista de um nível. Então ... no final você está obtendo uma lista de IDsda tabela2, na qual você vai usar para definir a condição table1, que será negada pela função de exclusão.

Harph
fonte
3
Eu também tive problemas com o construtor de lista [tabela2 ...] -> lista (tabela2 ...) funcionou para mim.
RickyA
3
correção: table1.objects.exclude (id__in = table2.objects.filter (your_condition) .values_list ('id', flat = True))
Richard
1
Estava tentando usar esta solução e encontrei um problema, então se acontecer com mais alguém ... Objs=Tbl1.objects.filter(...); IDs=Objs.values_list('id', flat=True); Objs.delete(); Tbl2.objects.filter(id__in=IDs')Isso não funcionou porque IDs é na verdade um objeto QuerySet. Quando eu apaguei as linhas das quais ele se originou, ele não funcionou mais com outras consultas. A solução é Tbl2.objects.filter(id__in=list(IDs))- transformá-lo em uma lista
Dakusan
1
Dependendo do contexto, se o filtro for como "tendo contagem (xx) == yy", é mais de 100x mais rápido de usar annotate()(timeit me deu 1,0497902309998608 vs 0,00514069400014705)
Olivier Pons
10

com estes modelos:

class table1(models.Model):
    field1 = models.CharField(max_length=10)      # a dummy field

class table2(models.Model):
    key_to_table1 = models.ForeignKey(table1)

você deve conseguir o que deseja usando:

table1.objects.exclude(table2=some_param)
Sergio Morstabilini
fonte
1
Isso ainda tem potencialmente puxando MUITOS registros do banco de dados desnecessariamente.
Jay Taylor
5
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])
ibz
fonte
1

Você pode escrever uma pesquisa personalizada para consultas Django:

Da documentação : "Vamos começar com uma pesquisa personalizada simples. Vamos escrever uma pesquisa personalizada ne que funciona em oposição ao exato . Author.objects.filter (name__ne = 'Jack') irá traduzir para o SQL: "author"."name" <> 'Jack'"

from django.db.models import Lookup

class NotEqual(Lookup):
    lookup_name = 'ne'

    def as_sql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = lhs_params + rhs_params
        return '%s <> %s' % (lhs, rhs), params
Blairg23
fonte
-16
[o1 for o1 in table1.objects.all() if o1.id not in [o2.id for o2 in table2.objects.filter(id=some_parm)]]

Ou melhor

not_in_ids = [obj.id for obj in table2.objects.filter(id=some_parm)]
selected_objects = [obj for obj in table1.objects.iterator() if obj.id not in not_in_ids]
Blue Peppers
fonte
12
Iterando em cada linha de uma tabela. gg
Rebs