J'ai toujours supposé que le chaînage de plusieurs appels filter () dans Django était toujours le même 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êtes 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êtes avec les filter()
appels enchaînés rejoint le modèle d'inventaire deux fois en créant effectivement un OU entre les deux conditions, tandis que le second jeu de requêtes ETs les deux conditions ensemble. Je m'attendais à ce que la première requête soit également 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 a-t-il un inconvénient à utiliser ".filter (). Filter (). Filter () ..." dans Django? semble indiquer que les deux ensembles de requêtes devraient être équivalents.
la source
further restrict
veut direless restrictive
?Ces deux styles de filtrage sont équivalents dans la plupart des cas, mais lorsque la requête sur des objets est basée sur ForeignKey ou ManyToManyField, ils sont légèrement différents.
Exemples tirés de la documentation .
Le modèle
Blog to Entry est une relation un-à-plusieurs.
objets
En supposant qu'il y ait des objets de blog et d'entrée ici.
requêtes
Pour la 1ère requête (filtre unique), elle ne correspond qu'à blog1.
Pour la deuxième requête (filtre en chaîne un), il filtre blog1 et blog2.
Le premier filtre restreint l'ensemble de requêtes à blog1, blog2 et blog5; le deuxième filtre restreint l'ensemble des blogs à blog1 et blog2.
Et tu devrais réaliser que
Donc, ce n'est pas la même chose, car Blog et Entrée sont des relations à valeurs multiples.
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, veuillez me corriger.
Edit: Changement de la v1.6 à la v1.8 car les liens 1.6 ne sont plus disponibles.
la source
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 soupçonner. C'est ainsi que sont placés les WHERE et JOIN.
Exemple1 (même table jointe):
(exemple tiré de https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships )
Cela vous donnera tous les blogs qui ont une entrée avec à la fois (entry_ headline _contains = 'Lennon') ET (entry__pub_date__year = 2008), ce que vous attendez de cette requête. Résultat: Réservez avec {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Exemple 2 (chaîné)
Cela couvrira tous les résultats de l'exemple 1, mais générera un peu plus de résultats. 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 qu'il vous donnera également des résultats tels que: Réservez avec {entry.headline: ' Lennon ', entry.pub_date: 2000}, {entry.headline: 'Bill', entry.pub_date: 2008 }
Dans ton cas
Je pense que c'est celui-ci dont vous avez besoin:
Et si vous souhaitez utiliser OU, veuillez lire: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects
la source
Parfois, vous ne voulez pas joindre plusieurs filtres ensemble comme ceci:
Et le code suivant ne renverrait en fait pas la bonne chose.
Ce que vous pouvez faire maintenant, c'est utiliser un filtre de comptage d'annotations.
Dans ce cas, nous comptons toutes les équipes qui appartiennent à un certain événement.
Ensuite, vous pouvez filtrer par annotation.
Cette solution est également moins chère sur les grands ensembles de requêtes.
J'espère que cela t'aides.
la source