Django dans / pas en requête

100

J'essaie de comprendre comment écrire une requête de style «pas dans» dans django. Par exemple, la structure de requête à laquelle je pense ressemblerait à ceci.

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

À quoi ressemblerait la syntaxe de django en supposant des modèles appelés table1 et table2?

Turbo
la source

Réponses:

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

La fonction d'exclusion fonctionne comme l' Notopérateur que vous avez demandé. L'attribut flat = Truedemande à la table2requête de renvoyer le value_listsous forme de liste à un niveau. Donc ... à la fin, vous obtenez une liste de IDsfrom table2, dans laquelle vous allez définir la condition table1, qui sera refusée par la fonction d'exclusion.

Harph
la source
3
J'ai également eu des problèmes avec le constructeur de liste [table2 ...] -> list (table2 ...) a fonctionné pour moi.
RickyA
3
correction: table1.objects.exclude (id__in = table2.objects.filter (your_condition) .values_list ('id', flat = True))
Richard
1
J'essayais d'utiliser cette solution et j'ai rencontré un problème, donc si cela arrive à quelqu'un d'autre ... Objs=Tbl1.objects.filter(...); IDs=Objs.values_list('id', flat=True); Objs.delete(); Tbl2.objects.filter(id__in=IDs')Cela n'a pas fonctionné car IDs est en fait un objet QuerySet. Lorsque j'ai supprimé les lignes dont il provenait, cela ne fonctionnait plus avec d'autres requêtes. La solution est Tbl2.objects.filter(id__in=list(IDs))- en faire une liste
Dakusan
1
Selon le contexte, si le filtre est comme "ayant count (xx) == yy" il est plus de 100 fois plus rapide à utiliser annotate()(timeit m'a donné 1.0497902309998608 vs 0.00514069400014705)
Olivier Pons
10

avec ces modèles:

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

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

vous devriez obtenir ce que vous voulez en utilisant:

table1.objects.exclude(table2=some_param)
Sergio Morstabilini
la source
1
Cela vous oblige encore à extraire beaucoup d'enregistrements de la base de données inutilement.
Jay Taylor
5
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])
ibz
la source
1

Vous pouvez écrire une recherche personnalisée pour les requêtes Django:

De la documentation : "Commençons par une simple recherche personnalisée. Nous allons écrire une recherche personnalisée ne qui fonctionne à l'opposé d' exact . Author.objects.filter (name__ne = 'Jack') se traduira par le 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
la source
-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 mieux

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]
Poivrons bleus
la source
12
Itérer sur chaque ligne d'un tableau. gg
Rebs