В чем разница между фильтром с несколькими аргументами и цепным фильтром в django?
В чем разница между фильтром с несколькими аргументами и цепным фильтром в django?
Ответы:
Как вы можете видеть в сгенерированных операторах SQL, разница не в «ИЛИ», как некоторые могут подумать. Так размещаются WHERE и JOIN.
Пример1 (та же объединенная таблица): из 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__headline__contains='Lennon') AND (entry__pub_date__year=2008)
, чего и следовало ожидать от этого запроса.
Результат:
Blog with {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Пример 2 (связанный)
Blog.objects.filter(
entry__headline__contains='Lennon'
).filter(
entry__pub_date__year=2008)
Это будет охватывать все результаты из примера 1, но даст немного больший результат. Потому что он сначала фильтрует все блоги, (entry__headline__contains='Lennon')
а затем фильтрует результаты (entry__pub_date__year=2008)
.
Разница в том, что он также даст такие результаты, как:
Один блог с несколькими записями
{entry.headline: '**Lennon**', entry.pub_date: 2000},
{entry.headline: 'Bill', entry.pub_date: **2008**}
Когда был оценен первый фильтр, книга включается из-за первой записи (даже если в ней есть другие записи, которые не совпадают). При оценке второго фильтра книга включается из-за второй записи.
Одна таблица: Но если запрос не включает объединенные таблицы, как в примере от Yuji и DTing. Результат такой же.
(entry__headline__contains='Lennon')
а затем фильтры результатов (entry__pub_date__year=2008)
» «Если« то из результата »верно, то почему оно будет включать что-то с entry.headline == 'Bill'
.. .не удалось entry__headline__contains='Lennon'
отфильтровать Bill
экземпляр?
Случай, в котором результаты «запрос-фильтр с несколькими аргументами» отличается от «запрос-фильтр-цепочки», следующий:
Выбор ссылочных объектов на основе ссылок на объекты и отношения осуществляется по принципу «один ко многим» (или «многие ко многим»).
Несколько фильтров:
Referenced.filter(referencing1_a=x, referencing1_b=y) # same referencing model ^^ ^^
Связанные фильтры:
Referenced.filter(referencing1_a=x).filter(referencing1_b=y)
Оба запроса могут выдавать разные результаты:
если более одной строки в referencing-modelReferencing1
могут ссылаться на одну и ту же строку в referenced-modelReferenced
. Это может быть в случаеReferenced
:Referencing1
иметь или 1: N (один ко многим) или N: M (многие ко многим) отношение корабль.
Пример:
Считайте, что в моем приложении my_company
есть две модели Employee
и Dependent
. У сотрудника my_company
может быть больше, чем иждивенцев (другими словами, иждивенец может быть сыном / дочерью одного сотрудника, в то время как у сотрудника может быть более одного сына / дочери).
Эхх, при условии, что как муж и жена, оба не могут работать в a my_company
. Я взял пример 1: м
Итак, Employee
это ссылочная модель, на которую может ссылаться больше, чем Dependent
ссылочная модель. Теперь рассмотрим состояние отношения следующим образом:
Employee: Dependent: +------+ +------+--------+-------------+--------------+ | name | | name | E-name | school_mark | college_mark | +------+ +------+--------+-------------+--------------+ | A | | a1 | A | 79 | 81 | | B | | b1 | B | 80 | 60 | +------+ | b2 | B | 68 | 86 | +------+--------+-------------+--------------+
Зависимый
a1
относится к сотрудникуA
, а зависимый -b1, b2
к сотрудникуB
.
Теперь мой запрос:
Найти всех сотрудников, у которых сын / дочь имеют отличительные знаки (скажем,> = 75%) как в колледже, так и в школе?
>>> Employee.objects.filter(dependent__school_mark__gte=75,
... dependent__college_mark__gte=75)
[<Employee: A>]
Результат «А» зависит от «А1» имеет отличительные знаки как в колледже, так и в школе зависит от сотрудника «А». Примечание «B» не выбрано, потому что ни один из детей «B» не имеет отличительных оценок как в колледже, так и в школе. Реляционная алгебра:
Сотрудник ⋈ (school_mark> = 75 И college_mark> = 75) В зависимости
Во-вторых, мне нужен запрос:
Найти всех сотрудников, некоторые из иждивенцев которых имеют отличия в колледже и школе?
>>> Employee.objects.filter(
... dependent__school_mark__gte=75
... ).filter(
... dependent__college_mark__gte=75)
[<Employee: A>, <Employee: B>]
На этот раз «B» также выбрана, потому что у «B» двое детей (более одного!), Один имеет отличительный знак в школе «b1», а другой имеет отличительный знак в колледже «b2».
Порядок фильтрации не имеет значения, мы также можем написать запрос выше как:
>>> Employee.objects.filter(
... dependent__college_mark__gte=75
... ).filter(
... dependent__school_mark__gte=75)
[<Employee: A>, <Employee: B>]
результат такой же! Реляционная алгебра может быть:
(Сотрудник ⋈ (school_mark> = 75) В зависимости) ⋈ (college_mark> = 75) В зависимости
Обратите внимание на следующее:
dq1 = Dependent.objects.filter(college_mark__gte=75, school_mark__gte=75)
dq2 = Dependent.objects.filter(college_mark__gte=75).filter(school_mark__gte=75)
Выводит тот же результат: [<Dependent: a1>]
Я проверяю целевой SQL-запрос, созданный Django, используя print qd1.query
иprint qd2.query
оба они одинаковы (Django 1.6).
Но семантически оба для меня разные . первый выглядит как простой раздел σ [school_mark> = 75 AND college_mark> = 75] (Dependent), а второй - как медленный вложенный запрос: σ [school_mark> = 75] (σ [College_mark> = 75] (Dependent)).
Если нужен код @codepad
Кстати, это приведено в документации @ Spanning многозначных отношений. Я только что добавил пример, думаю, это будет полезно для кого-то нового.
В большинстве случаев для запроса существует только один возможный набор результатов.
Использование цепочки фильтров возникает, когда вы имеете дело с m2m:
Учти это:
# will return all Model with m2m field 1
Model.objects.filter(m2m_field=1)
# will return Model with both 1 AND 2
Model.objects.filter(m2m_field=1).filter(m2m_field=2)
# this will NOT work
Model.objects.filter(Q(m2m_field=1) & Q(m2m_field=2))
Другие примеры приветствуются.
Разница в производительности огромна. Попробуйте и убедитесь.
Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)
на удивление медленнее по сравнению с
Model.objects.filter(condition_a, condition_b, condition_c)
Как упоминалось в Эффективном Django ORM ,
- QuerySets поддерживает состояние в памяти
- Цепочка триггеров клонирования, дублирование этого состояния
- К сожалению, QuerySets поддерживают много состояний
- Если возможно, не связывайте более одного фильтра
Вы можете использовать модуль подключения, чтобы просмотреть необработанные запросы sql для сравнения. Как объяснил Юджи, по большей части они эквивалентны, как показано здесь:
>>> from django.db import connection
>>> samples1 = Unit.objects.filter(color="orange", volume=None)
>>> samples2 = Unit.objects.filter(color="orange").filter(volume=None)
>>> list(samples1)
[]
>>> list(samples2)
[]
>>> for q in connection.queries:
... print q['sql']
...
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange AND `samples_unit`.`volume` IS NULL)
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange AND `samples_unit`.`volume` IS NULL)
>>>
Этот ответ основан на Django 3.1.
Окружающая обстановка
Модели
class Blog(models.Model):
blog_id = models.CharField()
class Post(models.Model):
blog_id = models.ForeignKeyField(Blog)
title = models.CharField()
pub_year = models.CharField() # Don't use CharField for date in production =]
Таблицы базы данных
Фильтры вызова
Blog.objects.filter(post__title="Title A", post__pub_year="2020")
# Result: <QuerySet [<Blog: 1>]>
Blog.objects.filter(post__title="Title A").filter(post_pub_date="2020)
# Result: <QuerySet [<Blog: 1>, [<Blog: 2>]>
Объяснение
Прежде чем я начну что-либо дальше, я должен заметить, что этот ответ основан на ситуации, когда для фильтрации объектов используется «ManyToManyField» или обратный «ForeignKey».
Если вы используете одну и ту же таблицу или «OneToOneField» для фильтрации объектов, то не будет никакой разницы между использованием «Фильтра по нескольким аргументам» или «Цепочки фильтров». Оба они будут работать как фильтр условия «И».
Самый простой способ понять, как использовать «Фильтр с несколькими аргументами» и «Цепочка фильтров», - это запомнить в фильтре «ManyToManyField» или обратном «ForeignKey», что «Фильтр с несколькими аргументами» является условием «И» и «Фильтр» -chain »является условием« ИЛИ ».
Причина, по которой «фильтр с несколькими аргументами» и «цепочка фильтров» настолько различаются, заключается в том, что они извлекают результат из разных таблиц соединения и используют разные условия в операторе запроса.
«Фильтр по нескольким аргументам» используйте «Post». «Public_Year» = '2020' для определения публичного года.
SELECT *
FROM "Book"
INNER JOIN ("Post" ON "Book"."id" = "Post"."book_id")
WHERE "Post"."Title" = 'Title A'
AND "Post"."Public_Year" = '2020'
В запросе к базе данных «цепочка фильтров» используется «T1». «Public_Year» = '2020' для определения публичного года.
SELECT *
FROM "Book"
INNER JOIN "Post" ON ("Book"."id" = "Post"."book_id")
INNER JOIN "Post" T1 ON ("Book"."id" = "T1"."book_id")
WHERE "Post"."Title" = 'Title A'
AND "T1"."Public_Year" = '2020'
Но почему разные условия влияют на результат?
Я считаю, что большинство из нас, кто заходит на эту страницу, включая меня =], придерживается того же предположения при использовании «Фильтра по нескольким аргументам» и «Цепочки фильтров».
Мы считаем, что результат должен быть получен из таблицы, подобной следующей, которая подходит для «Фильтра множественных аргументов». Так что, если вы используете «Фильтр множественных аргументов», вы получите результат, как и ожидали.
Но при работе с «цепочкой фильтров» Django создает другой оператор запроса, который изменяет приведенную выше таблицу на следующую. Кроме того, «Public Year» идентифицируется в разделе «T1» вместо раздела «Post» из-за изменения инструкции запроса.
Но откуда взялась эта странная диаграмма таблицы соединений «цепочка фильтров»?
Я не специалист по базам данных. Приведенное ниже объяснение - это то, что я понял до сих пор после того, как создал ту же структуру базы данных и провел тест с тем же оператором запроса.
На следующей диаграмме показано, откуда взялась эта странная диаграмма таблицы соединений «цепочка фильтров».
База данных сначала создаст объединенную таблицу, сопоставляя строки таблиц «Блог» и «Публикация» одну за другой.
После этого база данных снова выполняет тот же процесс сопоставления, но с использованием таблицы результатов шага 1 для сопоставления с таблицей «T1», которая является той же таблицей «Post».
Вот откуда взялась эта странная диаграмма таблицы соединений «цепочка фильтров».
Заключение
Таким образом, две вещи делают «фильтр с несколькими аргументами» и «цепочку фильтров» разными.
Грязный способ запомнить, как его использовать, - это «Фильтр с несколькими аргументами» - это условие «И», а «Цепочка фильтров» - это условие «ИЛИ» в фильтре «ManyToManyField» или обратном «ForeignKey».
Если вы попадаете на эту страницу в поисках того, как динамически создать набор запросов django с несколькими фильтрами цепочки, но вам нужно, чтобы фильтры были AND
типа, а не OR
, рассмотрите возможность использования Q-объектов .
Пример:
# First filter by type.
filters = None
if param in CARS:
objects = app.models.Car.objects
filters = Q(tire=param)
elif param in PLANES:
objects = app.models.Plane.objects
filters = Q(wing=param)
# Now filter by location.
if location == 'France':
filters = filters & Q(quay=location)
elif location == 'England':
filters = filters & Q(harbor=location)
# Finally, generate the actual queryset
queryset = objects.filter(filters)
Если требуются a и b, то
and_query_set = Model.objects.filter(a=a, b=b)
если требуется a, а также b, то
chaied_query_set = Model.objects.filter(a=a).filter(b=b)
Официальные документы: https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
Связанное сообщение: Объединение нескольких filter () в Django, это ошибка?
Есть разница, когда у вас есть запрос к связанному объекту, например
class Book(models.Model):
author = models.ForeignKey(Author)
name = models.ForeignKey(Region)
class Author(models.Model):
name = models.ForeignKey(Region)
запрос
Author.objects.filter(book_name='name1',book_name='name2')
возвращает пустой набор
и запросить
Author.objects.filter(book_name='name1').filter(book_name='name2')
возвращает авторов, у которых есть книги с именами "имя1" и "имя2"
подробнее см. https://docs.djangoproject.com/en/dev/topics/db/queries/#s-spanning-multi-valued-relationships
Author.objects.filter(book_name='name1',book_name='name2')
даже не валидный питон, это было быSyntaxError: keyword argument repeated