J’ai toujours supposé que le chaînage de plusieurs appels filter () dans Django était toujours la même chose que leur collecte en un seul appel.
# Equivalent Model.objects.filter(foo=1).filter(bar=2) Model.objects.filter(foo=1,bar=2)
mais j’ai rencontré un jeu de requête compliqué dans mon code où ce n’est pas le cas
class Inventory(models.Model): book = models.ForeignKey(Book) class Profile(models.Model): user = models.OneToOneField(auth.models.User) vacation = models.BooleanField() country = models.CharField(max_length=30) # Not Equivalent! Book.objects.filter(inventory__user__profile__vacation=False).filter(inventory__user__profile__country='BR') Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country='BR')
Le SQL généré est
SELECT "library_book"."id", "library_book"."asin", "library_book"."added", "library_book"."updated" FROM "library_book" INNER JOIN "library_inventory" ON ("library_book"."id" = "library_inventory"."book_id") INNER JOIN "auth_user" ON ("library_inventory"."user_id" = "auth_user"."id") INNER JOIN "library_profile" ON ("auth_user"."id" = "library_profile"."user_id") INNER JOIN "library_inventory" T5 ON ("library_book"."id" = T5."book_id") INNER JOIN "auth_user" T6 ON (T5."user_id" = T6."id") INNER JOIN "library_profile" T7 ON (T6."id" = T7."user_id") WHERE ("library_profile"."vacation" = False AND T7."country" = BR ) SELECT "library_book"."id", "library_book"."asin", "library_book"."added", "library_book"."updated" FROM "library_book" INNER JOIN "library_inventory" ON ("library_book"."id" = "library_inventory"."book_id") INNER JOIN "auth_user" ON ("library_inventory"."user_id" = "auth_user"."id") INNER JOIN "library_profile" ON ("auth_user"."id" = "library_profile"."user_id") WHERE ("library_profile"."vacation" = False AND "library_profile"."country" = BR )
Le premier jeu de requête avec les appels chaînés filter()
rejoint le modèle Inventory deux fois de manière efficace, créant un OR entre les deux conditions, tandis que le second groupe de requêtes AND réunit les deux conditions. Je m’attendais à ce que la première requête soit aussi ET les deux conditions. Est-ce le comportement attendu ou s’agit-il d’un bogue dans Django?
La réponse à une question connexe Y at-il un inconvénient à utiliser “.filter (). Filter (). Filter () …” dans Django? semble indiquer que les deux groupes de requêtes doivent être équivalents.
Si je comprends bien, ils sont subtilement différents par leur conception (et je suis certainement ouvert à la correction): le filter(A, B)
va d’abord filtrer selon A puis sous-filtre selon B, tandis que le filter(A).filter(B)
renverra une ligne correspondant à A ‘et’ une ligne potentiellement différente correspondant à B.
Regardez l’exemple ici:
https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
particulièrement:
Tout ce qui se trouve dans un seul appel de filtre () est appliqué simultanément pour filtrer les éléments correspondant à toutes ces exigences. Les appels de filtres successifs () restreignent davantage l’ensemble d’objects
…
Dans ce deuxième exemple (filtre (A) .filter (B)), le premier filtre restreint le jeu de requête à (A). Le deuxième filtre restreignait l’ensemble des blogs à ceux qui le sont également (B). Les entrées sélectionnées par le deuxième filtre peuvent ou non être identiques aux entrées du premier filtre. »
Ces deux styles de filtrage sont équivalents dans la plupart des cas, mais lorsque vous interrogez des objects basés sur ForeignKey ou ManyToManyField, ils sont légèrement différents.
Exemples de la documentation .
modèle
Blog to Entry est une relation un-à-plusieurs.
from django.db import models class Blog(models.Model): ... class Entry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) pub_date = models.DateField() ...
objects
En supposant qu’il y ait des objects de blog et d’entrée ici.
requêtes
Blog.objects.filter(entry__headline_contains='Lennon', entry__pub_date__year=2008) Blog.objects.filter(entry__headline_contains='Lennon').filter( entry__pub_date__year=2008)
Pour la 1ère requête (un seul filtre), elle ne correspond qu’à blog1.
Pour la 2ème requête (filtre en chaîne un), il filtre les blogs1 et blog2.
Le premier filtre restreint le jeu de requête à blog1, blog2 et blog5; le second filtre restreint l’ensemble des blogs à blog1 et blog2.
Et vous devriez réaliser que
Nous filtrons les éléments du blog avec chaque déclaration de filtre, pas les éléments d’entrée.
Donc, ce n’est pas la même chose, parce que Blog et Entry sont des relations à plusieurs valeurs.
Référence: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships
S’il y a quelque chose qui ne va pas, corrigez-moi s’il vous plaît.
Edit: Changé v1.6 à v1.8 puisque les liens 1.6 ne sont plus disponibles.
Comme vous pouvez le voir dans les instructions SQL générées, la différence n’est pas le “OU”, comme certains peuvent le penser. C’est comme ça que le WHERE et le JOIN sont placés.
(exemple de https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships )
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
Cela vous donnera tous les blogs qui ont une entrée avec les deux (entry_ headline _contains = ‘Lennon’) AND (entry__pub_date__year = 2008), ce que vous attendez de cette requête. Résultat: Livre avec {entry.headline: ‘Life of Lennon’, entry.pub_date: ‘2008’}
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
Cela couvrira tous les résultats de l’exemple 1, mais générera un peu plus de résultat. Parce qu’il filtre d’abord tous les blogs avec (entry_ headline _contains = ‘Lennon’), puis à partir des filtres de résultats (entry__pub_date__year = 2008).
La différence est que cela vous donnera aussi des résultats comme: Livre avec {entry.headline: ‘ Lennon ‘, entry.pub_date: 2000}, {entry.headline: ‘Bill’, entry.pub_date: 2008 }
Je pense que c’est celui-ci dont vous avez besoin:
Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country='BR')
Et si vous souhaitez utiliser OU, veuillez lire: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects