Подключение нескольких фильтров() в Django, это ошибка?
Я всегда предполагал, что цепочка нескольких вызовов filter() в Django всегда была такой же, как сбор их в одном вызове.
# Equivalent
Model.objects.filter(foo=1).filter(bar=2)
Model.objects.filter(foo=1,bar=2)
но я столкнулся со сложным queryset в моем коде, где это не так
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')
сгенерированный SQL-это
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 )
первый queryset с цепью filter() вызовы присоединяются к модели инвентаризации дважды эффективно создавая или между двумя условиями, в то время как второй queryset и два условия вместе. Я ожидал, что первый запрос будет также и два условия. Это ожидаемое поведение или это ошибка в Django?
ответ на связанный с этим вопрос есть обратная сторона использования ".фильтр.)(фильтр.)(фильтр.)(.."в Django? кажется, указано, что два запроса должны быть эквивалентны.
3 ответов:
насколько я понимаю, они тонко отличаются по дизайну (и я, безусловно, открыт для коррекции):
filter(A, B)будет сначала фильтровать в соответствии с A, а затем подфильтр в соответствии с B, в то время какfilter(A).filter(B)вернет строку, которая соответствует A ' и ' потенциально другой строке, которая соответствует B.посмотрите на пример здесь:
https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
в частности:
все внутри одного вызова filter () применяется одновременно для фильтрации элементов, соответствующих всем этим требованиям. Последовательные вызовы filter () дополнительно ограничивают набор объектов
...
в этом втором примере (фильтр (A).фильтр (B)), первый фильтр ограничил набор запросов (A). Второй фильтр ограничивал набор блогов далее теми, которые также являются (B). Записи, выбранные вторым фильтром, могут быть или не быть такими же, как записи в первом фильтре.-
эти два стиля фильтрации эквивалентны в большинстве случаев, но когда запрос на объекты базируется на ForeignKey или ManyToManyField, они немного отличаются.
примеры документация.
модель
Блог к записи-это отношение один ко многим.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() ...объекты
Предполагая, что здесь есть некоторые объекты блога и записи.запросы
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)для 1-го запроса (один фильтр один), он соответствует только blog1.
для 2-го запроса (цепные фильтры один), он отфильтровывает blog1 и blog2.
Первый фильтр ограничивает набор запросов blog1, blog2 и blog5; второй фильтр ограничивает набор блогов далее blog1 и blog2.и вы должны понимать, что
мы фильтруем элементы блога с каждым оператором фильтра, а не запись предметы.
Итак, это не одно и то же, потому что блог и запись являются многозначными отношениями.
Ссылка: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships
Если что-то не так, пожалуйста, поправьте меня.изменить: изменен v1.6 на v1.8, так как ссылки 1.6 больше не доступны.
как вы можете видеть в сгенерированных инструкциях SQL разница не является "или", как некоторые могут подозревать. Это то, как где и присоединиться помещается.
Example1 (та же объединенная таблица):
(пример из 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)Это даст вам все блоги, которые один запись с обеих (entry_заголовок_contains= 'Lennon') и (запись__pub_date__year=2008), что и следовало ожидать от этого запроса. Результат: Книга с {запись.заголовок: "жизнь Леннона", запись.pub_date: '2008'}
Пример 2 (цепочечной)
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)Это будет охватывать все результаты из примера 1, но он будет генерировать немного больше результата. Потому что он сначала фильтрует все блоги с (entry_заголовок_contains= 'Lennon'), а затем из фильтра результатов (запись__бар_дата__год=2008).
разница в том, что он также даст вам такие результаты, как: Книга с {запись.заголовок: 'Леннон', запись.pub_date: 2000}, {запись.заголовок: "Билл", запись.pub_date:2008}
в вашем случае
Я думаю, что это тот, который вам нужен:
Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country='BR')и если вы хотите использовать или читайте: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects

Comments