Django en / no en consulta

100

Estoy tratando de averiguar cómo escribir una consulta de estilo 'no en' en django. Por ejemplo, la estructura de consulta en la que estoy pensando se vería así.

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

¿Cómo se vería la sintaxis de django asumiendo modelos llamados table1 y table2?

Turbo
fuente

Respuestas:

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

La función de exclusión funciona como el Notoperador que solicitó. El atributo flat = Truele dice a la table2consulta que devuelva el value_listcomo una lista de un nivel. Entonces ... al final, está obteniendo una lista de IDstable2, en la que el usuario definirá la condición table1, que será denegada por la función de exclusión.

Harph
fuente
3
También tuve problemas con el constructor de listas [table2 ...] -> list (table2 ...) funcionó para mí.
RickyA
3
corrección: table1.objects.exclude (id__in = table2.objects.filter (your_condition) .values_list ('id', flat = True))
Richard
1
Estaba tratando de usar esta solución y encontré un problema, así que si le sucede a alguien más ... Objs=Tbl1.objects.filter(...); IDs=Objs.values_list('id', flat=True); Objs.delete(); Tbl2.objects.filter(id__in=IDs')Esto no funcionó porque ID es en realidad un objeto QuerySet. Cuando eliminé las filas desde las que se originó, ya no funcionó con otras consultas. La solución es Tbl2.objects.filter(id__in=list(IDs))- convertirlo en una lista
Dakusan
1
Dependiendo del contexto, si el filtro es como "tener recuento (xx) == yy", es más de 100 veces más rápido de usar annotate()(el tiempo me dio 1.0497902309998608 vs 0.00514069400014705)
Olivier Pons
10

con estos modelos:

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

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

deberías obtener lo que quieres usando:

table1.objects.exclude(table2=some_param)
Sergio Morstabilini
fuente
1
Esto todavía hace que pueda extraer MUCHOS registros de la base de datos innecesariamente.
Jay Taylor
5
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])
ibz
fuente
1

Puede escribir una búsqueda personalizada para consultas de Django:

De la documentación : "Comencemos con una búsqueda personalizada simple. Escribiremos una búsqueda personalizada ne que funcione de manera opuesta a exacta . Author.objects.filter (name__ne = 'Jack') se traducirá al 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
fuente
-16
[o1 for o1 in table1.objects.all() if o1.id not in [o2.id for o2 in table2.objects.filter(id=some_parm)]]

O mejor

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]
Pimientos azules
fuente
12
Iterando sobre cada fila de una tabla. gg
Rebs