Encadenando múltiples filtros () en Django, ¿es esto un error?

Siempre asumí que encadenar múltiples llamadas a filter () en Django era siempre lo mismo que recostackrlas 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 queryset 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 interrogaciones con las llamadas de filter() encadenado filter() se une al modelo de inventario dos veces creando de manera efectiva un OR entre las dos condiciones, mientras que el segundo conjunto de preguntas AND une las dos condiciones juntas. Estaba esperando que la primera consulta también Y las dos condiciones. ¿Es este el comportamiento esperado o es esto 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 consulta deberían ser equivalentes.

La forma en que lo entiendo es que son sutilmente diferentes por diseño (y ciertamente estoy abierto a la corrección): el filter(A, B) primero se filtrará según A y luego se subfiltrará según B, mientras que el filter(A).filter(B) devolverá una fila que coincida con A ‘y’ una fila potencialmente diferente que coincida con B.

Mira el ejemplo aquí:

https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

particularmente:

Todo lo que se encuentra dentro de una sola llamada de filtro () se aplica simultáneamente para filtrar los elementos que coinciden con todos esos requisitos. Las llamadas sucesivas de filtro () restringen aún más el conjunto de objetos

En este segundo ejemplo (filter (A) .filter (B)), el primer filtro restringió el queryset a (A). El segundo filtro restringió el conjunto de blogs a aquellos que también son (B). Las entradas seleccionadas por el segundo filtro pueden ser o no las mismas que en el primer filtro.

Estos dos estilos de filtrado son equivalentes en la mayoría de los casos, pero cuando la consulta en objetos se basa en ForeignKey o ManyToManyField, son ligeramente diferentes.

Ejemplos de la documentación .

modelo
Blog to Entry es una relación de uno a muchos.

 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() ... 

objetos
Suponiendo que hay algunos objetos de blog y entrada aquí.
enter image description here

consultas

 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) 

Para la primera consulta (filtro único uno), solo coincide con blog1.

Para la segunda consulta (filtros encadenados uno), filtra los blogs1 y blog2.
El primer filtro restringe el conjunto de preguntas a blog1, blog2 y blog5; el segundo filtro restringe el conjunto de blogs a blog1 y blog2.

Y deberías darte cuenta de eso

Estamos filtrando los artículos del Blog con cada statement de filtro, no los artículos de Entrada.

Por lo tanto, no es lo mismo, porque Blog y Entry son relaciones multivaluadas.

Referencia: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships
Si hay algo mal, por favor corrígeme.

Editar: Cambió v1.6 a v1.8 ya que los enlaces 1.6 ya no están disponibles.