with tutorial the español applications django django-orm

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



the django project (3)

Siempre asumí que encadenar múltiples llamadas a 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 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.


Como puede ver en las sentencias SQL generadas, la diferencia no es el "OR", como algunos pueden sospechar. Es cómo se coloca WHERE y JOIN.

Ejemplo 1 (la misma tabla unida):

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

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: Reserve con {entry.headline: ''Life of Lennon'', entry.pub_date: ''2008''}

Ejemplo 2 (encadenado)

Blog.objects.filter(entry__headline__contains=''Lennon'').filter(entry__pub_date__year=2008)

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: Libro con {entry.headline: '' Lennon '', entry.pub_date: 2000}, {entry.headline: ''Bill'', entry.pub_date: 2008 }

En tu caso

Creo que es este que necesitas:

Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country=''BR'')

Y si desea usar O, lea: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects


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í.

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 declaración 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.


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.