123 votes

Django dans / pas dans la requête

J'essaie de comprendre comment écrire une requête de type "not in" 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 que les modèles s'appellent table1 et table2 ?

193voto

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

La fonction d'exclusion fonctionne comme la fonction Not opérateur que vous avez demandé. L'attribut flat = True dit à table2 pour retourner le value_list comme une liste à un niveau. Donc... à la fin, vous obtenez une liste de IDs de la table 2, que vous allez utiliser pour définir la condition de la table 1. table1 qui seront refusés par la fonction d'exclusion.

3 votes

J'ai également eu des problèmes avec le constructeur de liste [table2...] -> list(table2...) a fonctionné pour moi.

3 votes

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

1 votes

J'ai essayé 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, il ne fonctionnait plus avec d'autres requêtes. La solution est la suivante Tbl2.objects.filter(id__in=list(IDs)) -- transformez-la en liste

12voto

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)

1 votes

Dans ce cas, vous pouvez encore extraire inutilement un grand nombre d'enregistrements de la base de données.

5voto

ibz Points 6855
table1.objects.extra(where=["table1.id NOT IN (SELECT table2.key_to_table1 FROM table2 WHERE table2.id = some_parm)"])

4voto

xpeiro Points 169

La réponse acceptée est "ok" mais je vais proposer une nouvelle approche qui est plus opérationnelle.

from django.db.models import Q

query = Q(id__in=table2.objects.filter(your_condition).values_list('id'))
table1.objects.filter(~query)

Si vous utilisez la clé primaire, l'appel à values_list() n'est pas nécessaire.

Le "~" de l'objet Q() agit comme l'opérateur "not".

Je suppose que cette approche rend le code plus réutilisable et vous permet de faire "n'importe quelle" sorte de choses dynamiques en stockant simplement vos objets Q() dans des variables.

1voto

Blairg23 Points 78

Vous pouvez écrire un lookup personnalisé pour les requêtes Django :

De la documentation : " Commençons par un simple lookup personnalisé. Nous allons écrire un lookup personnalisé ne qui fonctionne à l'inverse de 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X