різниця між фільтром з декількома аргументами та ланцюговим фільтром у django


Відповіді:


60

Як ви можете бачити в згенерованих операторах 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. Результат той самий.


20
Я припускаю, що я просто щільний сьогодні вранці, але це речення бентежить мене: "Оскільки спочатку він фільтрує всі блоги, (entry__headline__contains='Lennon')а потім з фільтрів результатів (entry__pub_date__year=2008)" Якщо ", то з результату" є точним, чому він буде включати щось із entry.headline == 'Bill'.. .не змогли entry__headline__contains='Lennon'відфільтрувати Billекземпляр?
Дастін Вайатт,

7
Я також розгублений. Здається, ця відповідь просто неправильна, але вона має 37 голосів проти ...
Personman

1
Ця відповідь вводить в оману та заплутує, зауважте, що вищезазначене є правильним лише при фільтрації за допомогою взаємозв’язків M2M, як зазначено у відповіді Юдзі. Ключовим моментом є приклад - фільтрація елементів блогу з кожним оператором фільтра, а не елементів запису.
диктор

1
Оскільки в одному блозі можливо кілька записів. Мова правильна. Поняття може заплутати, якщо ви не пам’ятаєте про всі рухомі частини.
DylanYoung

@DustinWyatt У мене теж були ті самі запитання, що і у вас, але я нарешті зрозумів! Будь ласка, перегляньте приклад працівника та утриманця, написаний Грієшем Чауханом, нижче на цій сторінці, і ви також отримаєте його.
theQuestionMan

33

Випадок, коли результати "декількох аргументів filter-query" відрізняються від "chained-filter-query", наступні:

Вибір об'єктів, на які посилаються, на основі посилань на об'єкти та відносини є один до багатьох (або багато до багатьох).

Кілька фільтрів:

    Referenced.filter(referencing1_a=x, referencing1_b=y)
    #  same referencing model   ^^                ^^

Прив’язані фільтри:

    Referenced.filter(referencing1_a=x).filter(referencing1_b=y)

Обидва запити можуть виводити різний результат:
Якщо більше, ніж один рядок у моделі Referencing1посилання може посилатися на той самий рядок у моделі, на яку посилається Referenced. Це може бути у випадку Referenced: Referencing1мати співвідношення 1: N (один до багатьох) або N: M (багато до багатьох).

Приклад:

Вважаємо, що моя заявка my_companyмає дві моделі Employeeі Dependent. Працівник my_companyможе мати більше, ніж утриманців (іншими словами, утриманцем може бути син / дочка одного працівника, тоді як працівник може мати більше одного сина / дочку).
Е-е, припускаючи, що як чоловік-дружина обидва не можуть працювати в 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>]

Результат - "A" залежно "a1" має відмінні знаки як в коледжі, так і в школі залежить від працівника "A". Примітка "B" не вибрано, оскільки нижча частина дитини "B" має знаки відмінності як в коледжі, так і в школі. Реляційна алгебра:

Співробітник (позначка_ школи> = 75 І позначка_ коледжу> = 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>]

результат однаковий! Реляційна алгебра може бути:

(Працівник (позначка_ школи> = 75) Залежний) ( позначка_коледжу > = 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).

Але семантично обидва для мене різні . перший виглядає як простий розділ σ [шкільна_марка> = 75 І позначка_коледжу> = 75] (Залежне), а друге як повільний вкладений запит: σ [позначка_ школи> = 75][Значок_коледжу> = 75] (Залежний)).

Якщо потрібен код @codepad

До речі, це подано в документації @ Розділення багатозначних відносин. Я щойно додав приклад, я думаю, це буде корисно для когось нового.


4
Дякую за це корисне пояснення, воно краще, ніж те, що є в документації, яке зовсім не зрозуміле.
wim

1
Остання позначка про безпосередню фільтрацію залежних людей надзвичайно корисна. Це показує, що зміна результатів остаточно відбувається лише тоді, коли ви переживаєте відносини багато-до-багатьох. Якщо ви безпосередньо надсилаєте запит до таблиці, ланцюгові фільтри подібно до прочісування двічі.
Кріс

20

Здебільшого для запиту існує лише один можливий набір результатів.

Використання ланцюгових фільтрів застосовується, коли ви маєте справу з 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))

Інші приклади вітаються.


4
Інший приклад: це не просто обмеження m2m, це може трапитися і з одним до багатьох - із зворотним пошуком, наприклад, використання пов’язаного імені на ForeignKey
wim

Дякуємо за пояснення! До цього я думав, що останній і другий приклади рівні, тому останній приклад для мене не працював (неправильні результати запитів), і я витратив багато часу на пошуки. 2-й приклад дуже корисний для мене. Також, як сказав Вім, це можна використовувати при зворотних відносинах "один до багатьох", як у моєму випадку.
zen11625

12

Різниця у продуктивності величезна. Спробуйте і подивіться.

Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)

є напрочуд повільним порівняно з

Model.objects.filter(condition_a, condition_b, condition_c)

Як згадувалося в Effective Django ORM ,

  • QuerySets підтримують стан у пам'яті
  • Зв’язування тригерів клонування, дублювання цього стану
  • На жаль, QuerySets підтримує багато стану
  • Якщо це можливо, не ланцюжком слід застосовувати більше одного фільтра

8

Ви можете використовувати модуль підключення, щоб побачити необроблені запити sql для порівняння. Як пояснили Yuji's, здебільшого вони еквівалентні, як показано тут:

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

2

Якщо ви в кінцевому підсумку на цій сторінці шукали як динамічно нарощувати Джанго QuerySet з декількома фільтрами СЦЕПЛЕНИЕ, але вам потрібні фільтри , щоб мати 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)

Якщо if або elif не передано, змінною фільтрів буде None, і тоді ви отримаєте TypeError: непідтримувані типи операндів для &: 'NoneType' і 'Q'. Я ініціював фільтри з фільтрами = Q ()
cwhisperer

0

Якщо потрібні a та b, тоді

and_query_set = Model.objects.filter(a=a, b=b)

якщо потрібні як, так і 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

Пов’язане повідомлення: Це ланцюжок кількох фільтрів () у Django, чи це помилка?


-4

Наприклад, є запит на пов’язаний об’єкт

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')

повертає авторів, у яких є книги з іменем "name1" і "name2"

детальніше див. на https://docs.djangoproject.com/en/dev/topics/db/queries/#s-spanning-multi-valued-relationships


5
Author.objects.filter(book_name='name1',book_name='name2')навіть не дійсний python, це будеSyntaxError: keyword argument repeated
wim

1
Де саме вказано ім'я книги? Ви маєте на увазі book_set__name?
DylanYoung
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.