різниця між фільтром з декількома аргументами та ланцюговим фільтром у 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.