Sempre presumi que encadear várias chamadas de filter () no Django era sempre o mesmo que coletá-las em uma única chamada.
# Equivalent
Model.objects.filter(foo=1).filter(bar=2)
Model.objects.filter(foo=1,bar=2)
mas eu encontrei um queryset complicado em meu código onde este não é o 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')
O SQL gerado é
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 )
O primeiro queryset com as filter()
chamadas encadeadas junta-se ao modelo Inventory duas vezes, criando efetivamente um OR entre as duas condições, enquanto o segundo queryset ANDs as duas condições juntas. Eu esperava que a primeira consulta também executasse AND nas duas condições. Este é o comportamento esperado ou é um bug no Django?
A resposta a uma pergunta relacionada Existe uma desvantagem em usar ".filter (). Filter (). Filter () ..." no Django? parece indicar que os dois querysets devem ser equivalentes.
fonte
further restrict
significaless restrictive
?Esses dois estilos de filtragem são equivalentes na maioria dos casos, mas quando consulta em objetos baseados em ForeignKey ou ManyToManyField, eles são ligeiramente diferentes.
Exemplos da documentação .
O modelo
Blog para entrada é uma relação um-para-muitos.
objetos
Supondo que haja alguns objetos de blog e de entrada aqui.
consultas
Para a primeira consulta (filtro único um), corresponde apenas a blog1.
Para a segunda consulta (filtros encadeados um), ele filtra blog1 e blog2.
O primeiro filtro restringe o queryset a blog1, blog2 e blog5; o segundo filtro restringe o conjunto de blogs ainda mais para blog1 e blog2.
E você deve perceber que
Portanto, não é a mesma coisa, porque Blog e Entry são relacionamentos de vários valores.
Referência: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships
Se houver algo errado, por favor me corrija.
Editar: v1.6 alterado para v1.8 uma vez que os links 1.6 não estão mais disponíveis.
fonte
Como você pode ver nas instruções SQL geradas, a diferença não é o "OU", como alguns podem suspeitar. É como WHERE e JOIN são colocados.
Exemplo1 (mesma tabela associada):
(exemplo de https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships )
Isso lhe dará todos os Blogs que têm uma entrada com ambos (entrada_ título _contains = 'Lennon') E (entry__pub_date__year = 2008), que é o que você esperaria desta consulta. Resultado: Reserve com {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Exemplo 2 (encadeado)
Isso cobrirá todos os resultados do Exemplo 1, mas irá gerar um pouco mais de resultados. Porque primeiro filtra todos os blogs com (entry_ headline _contains = 'Lennon') e depois dos filtros de resultado (entry__pub_date__year = 2008).
A diferença é que ele também fornecerá resultados como: Reserve com {entry.headline: ' Lennon ', entry.pub_date: 2000}, {entry.headline: 'Bill', entry.pub_date: 2008 }
No seu caso
Acho que é este que você precisa:
E se você deseja usar OR, leia: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects
fonte
Às vezes, você não deseja unir vários filtros como este:
E o código a seguir realmente não retornaria a coisa correta.
O que você pode fazer agora é usar um filtro de contagem de anotação.
Neste caso, contamos todos os turnos que pertencem a um determinado evento.
Depois, você pode filtrar por anotação.
Esta solução também é mais barata em grandes querysets.
Espero que isto ajude.
fonte