Siempre asumí que encadenar múltiples llamadas filter () en Django era siempre lo mismo que recopilarlas en una sola llamada.
# Equivalent
Model.objects.filter(foo=1).filter(bar=2)
Model.objects.filter(foo=1,bar=2)
pero me he encontrado con un conjunto de consultas complicado en mi código donde este no es el caso
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')
El SQL generado es
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 )
El primer conjunto de consultas con las filter()
llamadas encadenadas se une al modelo de Inventario dos veces, creando un OR entre las dos condiciones, mientras que el segundo conjunto de consultas aplica un AND a las dos condiciones juntas. Esperaba que la primera consulta también Y las dos condiciones. ¿Es este el comportamiento esperado o es un error en Django?
La respuesta a una pregunta relacionada ¿Hay alguna desventaja en el uso de ".filter (). Filter (). Filter () ..." en Django? parece indicar que los dos conjuntos de consultas deberían ser equivalentes.
fuente
further restrict
significaless restrictive
?Estos dos estilos de filtrado son equivalentes en la mayoría de los casos, pero cuando se consultan objetos basados en ForeignKey o ManyToManyField, son ligeramente diferentes.
Ejemplos de la documentación .
El modelo
Blog to Entry es una relación de uno a varios.
objetos
Suponiendo que hay algunos objetos de entrada y blog aquí.
consultas
Para la primera consulta (filtro único uno), solo coincide con blog1.
Para la segunda consulta (filtros encadenados uno), filtra blog1 y blog2.
El primer filtro restringe el conjunto de consultas a blog1, blog2 y blog5; el segundo filtro restringe el conjunto de blogs a blog1 y blog2.
Y deberías darte cuenta de que
Entonces, no es lo mismo, porque Blog y Entry son relaciones de valores múltiples.
Referencia: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships
Si hay algún problema, corrígeme.
Editar: Se cambió v1.6 a v1.8 ya que los enlaces 1.6 ya no están disponibles.
fuente
Como puede ver en las declaraciones SQL generadas, la diferencia no es el "OR" como algunos sospechan. Así es como se coloca WHERE y JOIN.
Example1 (misma tabla unida):
(ejemplo de https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships )
Esto le dará todos los blogs que tienen una entrada con ambos (entry_ headline _contains = 'Lennon') Y (entry__pub_date__year = 2008), que es lo que esperaría de esta consulta. Resultado: libro con {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Ejemplo 2 (encadenado)
Esto cubrirá todos los resultados del Ejemplo 1, pero generará un poco más de resultado. Porque primero filtra todos los blogs con (entry_ headline _contains = 'Lennon') y luego desde los filtros de resultados (entry__pub_date__year = 2008).
La diferencia es que también le dará resultados como: Reserve con {entry.headline: ' Lennon ', entry.pub_date: 2000}, {entry.headline: 'Bill', entry.pub_date: 2008 }
En tu caso
Creo que es este lo que necesitas:
Y si desea usar O, lea: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects
fuente
A veces no desea unir varios filtros de esta manera:
Y el siguiente código en realidad no devolvería lo correcto.
Lo que puede hacer ahora es utilizar un filtro de recuento de anotaciones.
En este caso contamos todos los turnos que pertenecen a un determinado evento.
Luego, puede filtrar por anotación.
Esta solución también es más económica en conjuntos de consultas grandes.
Espero que esto ayude.
fuente