Jaka jest różnica między filtrem z wieloma argumentami a filtrem łańcuchowym w django?
python
django
django-models
testmobile
źródło
źródło
Odpowiedzi:
Jak widać w wygenerowanych instrukcjach SQL, różnica nie polega na „LUB”, jak niektórzy mogą podejrzewać. Tak jest umieszczane GDZIE i DOŁĄCZ.
Przykład 1 (ta sama połączona tabela): z 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)
Spowoduje to wyświetlenie wszystkich blogów, które mają jeden wpis z obydwoma
(entry__headline__contains='Lennon') AND (entry__pub_date__year=2008)
, czego można oczekiwać od tego zapytania.Wynik:
Blog with {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Przykład 2 (przykuty)
Blog.objects.filter( entry__headline__contains='Lennon' ).filter( entry__pub_date__year=2008)
To obejmie wszystkie wyniki z przykładu 1, ale wygeneruje nieco więcej wyników. Ponieważ najpierw filtruje wszystkie blogi z
(entry__headline__contains='Lennon')
filtrami wyników, a następnie z nich(entry__pub_date__year=2008)
.Różnica polega na tym, że da Ci również wyniki takie jak:
Pojedynczy blog z wieloma wpisami
{entry.headline: '**Lennon**', entry.pub_date: 2000}, {entry.headline: 'Bill', entry.pub_date: **2008**}
Podczas oceny pierwszego filtru książka jest uwzględniana ze względu na pierwszy wpis (nawet jeśli zawiera inne pozycje, które nie są zgodne). Gdy oceniany jest drugi filtr, książka jest uwzględniana z powodu drugiego wpisu.
Jedna tabela: ale jeśli zapytanie nie obejmuje tabel połączonych, jak w przykładzie z Yuji i DTing. Wynik jest taki sam.
źródło
(entry__headline__contains='Lennon')
filtrami wyników, a potem z filtrów wyników(entry__pub_date__year=2008)
”, jeśli „to z wyniku” jest trafne, dlaczego ma zawierać coś zentry.headline == 'Bill'
… . nieentry__headline__contains='Lennon'
odfiltrowałbyBill
instancji?Przypadek, w którym wyniki „zapytania filtrującego z wieloma argumentami” różnią się od wyników zapytania „zapytanie filtru łańcuchowego”, a więc:
Przykład:
Rozważmy, że moja aplikacja
my_company
ma dwa modeleEmployee
iDependent
. Pracownikmy_company
może mieć więcej niż osoby pozostające na utrzymaniu (innymi słowy, na utrzymaniu może być syn / córka jednego pracownika, podczas gdy pracownik może mieć więcej niż jednego syna / córkę).Ech, zakładając, że jak mąż-żona oboje nie mogą pracować w
my_company
. Wziąłem przykład 1: mJest to więc
Employee
model odniesienia, do którego może się odwoływać więcej niżDependent
model odniesienia. Rozważmy teraz stan relacji w następujący sposób:Teraz moje zapytanie to:
Czy znaleźć wszystkich pracowników, którzy mają syna / córkę, mają wyróżnienia (powiedzmy> = 75%) na uczelni i w szkole?
>>> Employee.objects.filter(dependent__school_mark__gte=75, ... dependent__college_mark__gte=75) [<Employee: A>]
Wynik to „A” na utrzymaniu „a1” ma wyróżnienia zarówno na uczelni, jak i szkoła jest zależna od pracownika „A”. Uwaga „B” nie została wybrana, ponieważ dziecko „B” nie ma wyróżnień zarówno na uczelni, jak iw szkole. Algebra relacyjna:
Po drugie, potrzebuję zapytania:
Znaleźć wszystkich pracowników, których niektóre osoby pozostające na ich utrzymaniu mają wyróżnienia na uczelni lub w szkole?
>>> Employee.objects.filter( ... dependent__school_mark__gte=75 ... ).filter( ... dependent__college_mark__gte=75) [<Employee: A>, <Employee: B>]
Tym razem „B” również zostało wybrane, ponieważ „B” ma dwoje dzieci (więcej niż jedno!), Jedno ma wyróżnienie w szkole „b1”, a drugie ma wyróżnienie w szkole „b2”.
Kolejność filtrów nie ma znaczenia, powyższe zapytanie możemy również zapisać jako:
>>> Employee.objects.filter( ... dependent__college_mark__gte=75 ... ).filter( ... dependent__school_mark__gte=75) [<Employee: A>, <Employee: B>]
wynik jest taki sam! Algebra relacyjna może być:
Uwaga:
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)
Daje ten sam wynik:
[<Dependent: a1>]
Sprawdzam docelowe zapytanie SQL wygenerowane przez Django przy użyciu
print qd1.query
iprint qd2.query
oba są takie same (Django 1.6).Ale semantycznie oba są dla mnie różne . najpierw wygląda jak prosta sekcja σ [school_mark> = 75 AND college_mark> = 75] (zależna), a druga jak powolne zagnieżdżone zapytanie: σ [school_mark> = 75] (σ [college_mark> = 75] (zależne)).
Jeśli potrzebujesz Code @codepad
btw, jest to podane w dokumentacji @ Spanning relacji wielowartościowych Właśnie dodałem przykład, myślę, że będzie pomocny dla kogoś nowego.
źródło
W większości przypadków dla zapytania istnieje tylko jeden możliwy zestaw wyników.
Zastosowanie do łączenia filtrów pojawia się, gdy masz do czynienia z m2m:
Rozważ to:
# 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))
Inne przykłady są mile widziane.
źródło
Różnica w wydajności jest ogromna. Spróbuj i zobacz.
Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)
jest zaskakująco wolny w porównaniu z
Model.objects.filter(condition_a, condition_b, condition_c)
Jak wspomniano w Effective Django ORM ,
źródło
Możesz użyć modułu połączenia, aby zobaczyć nieprzetworzone zapytania sql do porównania. Jak wyjaśnił Yuji, w większości są one równoważne, jak pokazano tutaj:
>>> 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) >>>
źródło
Ta odpowiedź jest oparta na Django 3.1.
Środowisko
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>]>
Wyjaśnienie
Zanim zacznę cokolwiek dalej, muszę zauważyć, że ta odpowiedź opiera się na sytuacji, w której do filtrowania obiektów używa się „ManyToManyField” lub odwrotnego „ForeignKey”.
Jeśli używasz tej samej tabeli lub „OneToOneField” do filtrowania obiektów, nie będzie różnicy między używaniem „Filtra wielu argumentów” lub „Łańcucha filtrów”. Oba będą działać jak filtr warunku „AND”.
Prostym sposobem na zrozumienie, jak używać „Filtra wielu argumentów” i „Łańcucha filtrów”, jest zapamiętanie w filtrze „ManyToManyField” lub odwrotnym filtrze „ForeignKey”, „Filtr wielu argumentów” to warunek „AND”, a „Filter” -chain ”jest warunkiem„ LUB ”.
Powodem, dla którego „Filtr wielu argumentów” i „Łańcuch filtrów” są tak różne, jest to, że pobierają one wynik z innej tabeli łączenia i używają innego warunku w instrukcji zapytania.
SELECT * FROM "Book" INNER JOIN ("Post" ON "Book"."id" = "Post"."book_id") WHERE "Post"."Title" = 'Title A' AND "Post"."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'
Ale dlaczego różne warunki wpływają na wynik?
Wydaje mi się, że większość z nas, którzy przychodzą na tę stronę, w tym ja =], ma to samo założenie, używając na początku „filtru wielu argumentów” i „łańcucha filtrów”.
Który naszym zdaniem wynik powinien zostać pobrany z tabeli, takiej jak następująca, która jest poprawna dla „Filtra wielu argumentów”. Jeśli więc używasz „Filtra wielu argumentów”, otrzymasz wynik zgodny z oczekiwaniami.
Ale podczas zajmowania się „łańcuchem filtrów” Django tworzy inną instrukcję zapytania, która zmienia powyższą tabelę na następną. Ponadto „Rok publiczny” jest identyfikowany w sekcji „T1” zamiast w sekcji „Opublikuj” z powodu zmiany wyrażenia zapytania.
Ale skąd pochodzi ten dziwny diagram tabeli łączenia filtrów z łańcuchem filtrów?
Nie jestem ekspertem od baz danych. Poniższe wyjaśnienie jest tym, co rozumiem do tej pory po utworzeniu tej samej struktury bazy danych i wykonaniu testu z tą samą instrukcją zapytania.
Poniższy diagram pokaże, skąd wziął się ten dziwny diagram tabeli łączenia łańcuchów filtrów.
Baza danych najpierw utworzy tabelę łączenia, dopasowując kolejno wiersze tabel „Blog” i „Post”.
Następnie baza danych ponownie wykonuje ten sam proces dopasowywania, ale wykorzystuje tabelę wyników kroku 1 do dopasowania tabeli „T1”, która jest po prostu tą samą tabelą „Post”.
I stąd wziął się ten dziwny diagram tabeli złączeń typu „łańcuch filtrów”.
Wniosek
Dwie rzeczy sprawiają, że „Filtr wielu argumentów” i „Łańcuch filtrów” są różne.
Brudnym sposobem na zapamiętanie, jak go używać, jest to, że „Filtr wielu argumentów” to warunek „AND” , a „łańcuch filtrów” to warunek „LUB” , gdy znajduje się w filtrze „ManyToManyField” lub odwrotnym filtrze „ForeignKey”.
źródło
Jeśli znajdziesz się na tej stronie, szukając sposobu dynamicznego tworzenia zestawu zapytań django z wieloma filtrami łańcuchowymi, ale potrzebujesz filtrów tego
AND
typu zamiastOR
, rozważ użycie obiektów Q .Przykład:
# 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)
źródło
Jeśli wymaga a i b, to
and_query_set = Model.objects.filter(a=a, b=b)
jeśli wymaga a, jak b, to
chaied_query_set = Model.objects.filter(a=a).filter(b=b)
Oficjalne dokumenty: https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
Powiązany post: Łączenie wielu filtrów () w Django, czy to błąd?
źródło
Na przykład istnieje różnica, gdy masz żądanie dotyczące powiązanego obiektu
class Book(models.Model): author = models.ForeignKey(Author) name = models.ForeignKey(Region) class Author(models.Model): name = models.ForeignKey(Region)
żądanie
Author.objects.filter(book_name='name1',book_name='name2')
zwraca pusty zestaw
i prośba
Author.objects.filter(book_name='name1').filter(book_name='name2')
zwraca autorów, którzy mają książki z „imieniem1” i „imieniem2”
po szczegóły zajrzyj na https://docs.djangoproject.com/en/dev/topics/db/queries/#s-spanning-multi-valued-relationships
źródło
Author.objects.filter(book_name='name1',book_name='name2')
nie jest nawet poprawnym Pythonem, byłoby toSyntaxError: keyword argument repeated