Was ist der Unterschied zwischen Filter mit mehreren Argumenten und Kettenfilter in Django?
Antworten:
Wie Sie in den generierten SQL-Anweisungen sehen können, ist der Unterschied nicht das "ODER", wie manche vermuten. So wird WHERE und JOIN platziert.
Beispiel 1 (gleiche verknüpfte Tabelle): von https://docs.djangoproject.com/de/dev/topics/db/queries/#spanning-multi-valued-relationships
Blog.objects.filter(
entry__headline__contains='Lennon',
entry__pub_date__year=2008)
Dadurch erhalten Sie alle Blogs, die einen Eintrag mit beiden haben (entry__headline__contains='Lennon') AND (entry__pub_date__year=2008)
, was Sie von dieser Abfrage erwarten würden.
Ergebnis:
Blog with {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}
Beispiel 2 (verkettet)
Blog.objects.filter(
entry__headline__contains='Lennon'
).filter(
entry__pub_date__year=2008)
Dies deckt alle Ergebnisse aus Beispiel 1 ab, erzeugt jedoch etwas mehr Ergebnisse. Weil es zuerst alle Blogs mit (entry__headline__contains='Lennon')
und dann aus den Ergebnisfiltern filtert (entry__pub_date__year=2008)
.
Der Unterschied besteht darin, dass Sie auch folgende Ergebnisse erhalten:
Ein einzelner Blog mit mehreren Einträgen
{entry.headline: '**Lennon**', entry.pub_date: 2000},
{entry.headline: 'Bill', entry.pub_date: **2008**}
Wenn der erste Filter ausgewertet wurde, ist das Buch aufgrund des ersten Eintrags enthalten (obwohl es andere Einträge enthält, die nicht übereinstimmen). Wenn der zweite Filter ausgewertet wird, wird das Buch aufgrund des zweiten Eintrags aufgenommen.
Eine Tabelle: Wenn die Abfrage jedoch keine verknüpften Tabellen wie das Beispiel von Yuji und DTing umfasst. Das Ergebnis ist das gleiche.
(entry__headline__contains='Lennon')
und dann aus den Ergebnisfiltern filtert (entry__pub_date__year=2008)
" Wenn "dann aus dem Ergebnis" korrekt ist, warum wird er etwas mit ... enthalten entry.headline == 'Bill'
? .wollte entry__headline__contains='Lennon'
die Bill
Instanz nicht herausfiltern ?
Der Fall, in dem sich die Ergebnisse von "Filterabfrage mit mehreren Argumenten" von "verketteter Filterabfrage" unterscheiden, ist folgender:
Die Auswahl von referenzierten Objekten auf der Grundlage von referenzierenden Objekten und Beziehungen erfolgt eins zu viele (oder viele zu viele).
Mehrere Filter:
Referenced.filter(referencing1_a=x, referencing1_b=y) # same referencing model ^^ ^^
Verkettete Filter:
Referenced.filter(referencing1_a=x).filter(referencing1_b=y)
Beide Abfragen können unterschiedliche Ergebnisse ausgeben:
Wenn mehr als eineReferencing1
Zeile im Referenzierungsmodell auf dieselbe Zeile im Referenzierungsmodell verweisen kannReferenced
. Dies kann der Fall sein inReferenced
:Referencing1
entweder 1: N (eins zu viele) oder N: M (viele zu viele) Beziehung haben.
Beispiel:
Betrachten Sie meine Anwendung my_company
hat zwei Modelle Employee
und Dependent
. Ein Mitarbeiter in my_company
kann mehr als abhängige Personen haben (mit anderen Worten, ein abhängiger Mitarbeiter kann Sohn / Tochter eines einzelnen Mitarbeiters sein, während ein Mitarbeiter mehr als einen Sohn / eine Tochter haben kann).
Ehh, vorausgesetzt, wie Ehemann-Ehefrau können beide nicht in einem arbeiten my_company
. Ich habe 1: m Beispiel genommen
Es Employee
handelt sich also um ein Referenzmodell, auf das von mehr als dem Referenzierungsmodell Dependent
verwiesen werden kann. Betrachten Sie nun den Beziehungsstatus wie folgt:
Employee: Dependent: +------+ +------+--------+-------------+--------------+ | name | | name | E-name | school_mark | college_mark | +------+ +------+--------+-------------+--------------+ | A | | a1 | A | 79 | 81 | | B | | b1 | B | 80 | 60 | +------+ | b2 | B | 68 | 86 | +------+--------+-------------+--------------+
Abhängig
a1
bezieht sich auf MitarbeiterA
und abhängigb1, b2
Verweise auf MitarbeiterB
.
Jetzt ist meine Frage:
Finden Sie alle Mitarbeiter, deren Sohn / Tochter sowohl im College als auch in der Schule Unterscheidungsmerkmale (sagen wir> = 75%) hat?
>>> Employee.objects.filter(dependent__school_mark__gte=75,
... dependent__college_mark__gte=75)
[<Employee: A>]
Ausgabe ist 'A' abhängig 'a1' hat sowohl im College als auch in der Schule Unterscheidungsmerkmale und ist abhängig von Mitarbeiter 'A'. Hinweis 'B' ist nicht ausgewählt, da keiner der Kinder von 'B' sowohl im College als auch in der Schule Unterscheidungsmerkmale aufweist. Relationale Algebra:
Mitarbeiter ⋈ (Schulzeichen> = 75 UND Hochschulzeichen> = 75) Abhängig
Im zweiten Fall benötige ich eine Abfrage:
Finden Sie alle Mitarbeiter, deren Angehörige im College und in der Schule Unterscheidungsmerkmale haben?
>>> Employee.objects.filter(
... dependent__school_mark__gte=75
... ).filter(
... dependent__college_mark__gte=75)
[<Employee: A>, <Employee: B>]
Dieses Mal wurde 'B' auch ausgewählt, weil 'B' zwei Kinder hat (mehr als eines!), Eines in der Schule 'b1' und das andere im College 'b2' ein Unterscheidungszeichen hat.
Die Reihenfolge des Filters spielt keine Rolle. Wir können die obige Abfrage auch wie folgt schreiben:
>>> Employee.objects.filter(
... dependent__college_mark__gte=75
... ).filter(
... dependent__school_mark__gte=75)
[<Employee: A>, <Employee: B>]
Ergebnis ist das gleiche! Relationale Algebra kann sein:
(Mitarbeiter ⋈ (Schulzeichen> = 75) Abhängig) ⋈ (Hochschulzeichen> = 75) Abhängig
Beachten Sie Folgendes:
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)
Gibt das gleiche Ergebnis aus: [<Dependent: a1>]
Ich überprüfe die von Django generierte SQL-Zielabfrage mit print qd1.query
und print qd2.query
beide sind gleich (Django 1.6).
Aber semantisch unterscheiden sich beide von mir . Erstens sieht es aus wie ein einfacher Abschnitt σ [school_mark> = 75 AND college_mark> = 75] (abhängig) und zweitens wie eine langsam verschachtelte Abfrage: σ [school_mark> = 75] (σ [college_mark> = 75] (abhängig)).
Wenn man Code @codepad braucht
Übrigens ist es in der Dokumentation @ Spanning mehrwertiger Beziehungen angegeben. Ich habe gerade ein Beispiel hinzugefügt. Ich denke, es wird für jemanden hilfreich sein, der neu ist.
In den meisten Fällen gibt es nur eine mögliche Ergebnismenge für eine Abfrage.
Die Verwendung für Verkettungsfilter kommt, wenn Sie mit m2m arbeiten:
Bedenken Sie:
# 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))
Andere Beispiele sind willkommen.
Der Leistungsunterschied ist enorm. Probieren Sie es aus und sehen Sie.
Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)
ist überraschend langsam im Vergleich zu
Model.objects.filter(condition_a, condition_b, condition_c)
Wie in Effective Django ORM erwähnt ,
- QuerySets behalten den Status im Speicher bei
- Das Verketten löst das Klonen aus und dupliziert diesen Status
- Leider behalten QuerySets viel Status bei
- Wenn möglich, verketten Sie nicht mehr als einen Filter
Sie können das Verbindungsmodul verwenden, um die zu vergleichenden SQL-Abfragen anzuzeigen. Wie von Yuji erklärt, sind sie größtenteils gleichwertig, wie hier gezeigt:
>>> 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)
>>>
Wenn Sie auf dieser Seite nach Möglichkeiten suchen, ein Django-Abfrageset mit mehreren Verkettungsfiltern dynamisch aufzubauen, die Filter jedoch vom AND
Typ sein OR
müssen, sollten Sie Q-Objekte verwenden .
Ein Beispiel:
# 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)
Diese Antwort basiert auf Django 3.1.
Umgebung
Modelle
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 =]
Datenbanktabellen
Filter rufen auf
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>]>
Erläuterung
Bevor ich etwas weiter anfange, muss ich feststellen, dass diese Antwort auf der Situation basiert, in der "ManyToManyField" oder ein umgekehrter "ForeignKey" zum Filtern von Objekten verwendet wird.
Wenn Sie dieselbe Tabelle oder ein "OneToOneField" zum Filtern von Objekten verwenden, gibt es keinen Unterschied zwischen der Verwendung von "Filter für mehrere Argumente" oder "Filterkette". Beide funktionieren wie ein "UND" -Bedingungsfilter.
Der einfache Weg, um zu verstehen, wie "Filter für mehrere Argumente" und "Filterkette" verwendet werden, besteht darin, sich in einem "ManyToManyField" - oder einem umgekehrten "ForeignKey" -Filter zu merken, dass "Filter für mehrere Argumente" eine "UND" -Bedingung und "Filter" ist -chain "ist eine" ODER "-Bedingung.
Der Grund, warum "Filter für mehrere Argumente" und "Filterkette" so unterschiedlich sind, liegt darin, dass sie Ergebnisse aus unterschiedlichen Verknüpfungstabellen abrufen und unterschiedliche Bedingungen in der Abfrageanweisung verwenden.
"Filter für mehrere Argumente" verwendet "Post". "Public_Year" = '2020' , um das öffentliche Jahr zu identifizieren
SELECT *
FROM "Book"
INNER JOIN ("Post" ON "Book"."id" = "Post"."book_id")
WHERE "Post"."Title" = 'Title A'
AND "Post"."Public_Year" = '2020'
Die Datenbankabfrage "Filterkette" verwendet "T1". "Public_Year" = '2020' , um das öffentliche Jahr zu identifizieren
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'
Aber warum wirken sich unterschiedliche Bedingungen auf das Ergebnis aus?
Ich glaube, die meisten von uns, die auf diese Seite kommen, einschließlich mir =], haben die gleiche Annahme, wenn sie zuerst "Filter für mehrere Argumente" und "Filterkette" verwenden.
Wir glauben, dass das Ergebnis aus einer Tabelle wie der folgenden abgerufen werden sollte, die für "Filter für mehrere Argumente" korrekt ist. Wenn Sie also "Filter für mehrere Argumente" verwenden, erhalten Sie ein Ergebnis wie erwartet.
Während Django sich mit der "Filterkette" befasst, erstellt er eine andere Abfrageanweisung, die die obige Tabelle in die folgende ändert. Außerdem wird das "öffentliche Jahr" aufgrund der Änderung der Abfrageanweisung im Abschnitt "T1" anstelle des Abschnitts "Post" angegeben.
Aber woher kommt dieses seltsame "Filter-Chain" -Verbindungs-Tabellendiagramm?
Ich bin kein Datenbankexperte. Die folgende Erklärung ist das, was ich bisher verstanden habe, nachdem ich dieselbe Struktur der Datenbank erstellt und einen Test mit derselben Abfrageanweisung durchgeführt habe.
Das folgende Diagramm zeigt, wie dieses seltsame "Filter-Chain" -Verbindungs-Tabellendiagramm stammt.
Die Datenbank erstellt zunächst eine Verknüpfungstabelle, indem die Zeilen der Tabellen "Blog" und "Post" nacheinander abgeglichen werden.
Danach führt die Datenbank nun erneut denselben Abgleich durch, verwendet jedoch die Ergebnistabelle von Schritt 1, um mit der Tabelle "T1" übereinzustimmen, die genau dieselbe "Post" -Tabelle ist.
Und so kommt dieses seltsame "Filter-Chain" -Verbindungs-Tabellendiagramm.
Fazit
Zwei Dinge unterscheiden also "Filter für mehrere Argumente" und "Filterkette".
Die schmutzige Art, sich daran zu erinnern, wie man es benutzt, ist "Multiple Arguments Filter" ist eine "AND" -Bedingung und "Filter-chain" ist eine "OR" -Bedingung, während in einem "ManyToManyField" - oder einem umgekehrten "ForeignKey" -Filter.
Wenn erfordert a und b dann
and_query_set = Model.objects.filter(a=a, b=b)
wenn erfordert a sowie b dann
chaied_query_set = Model.objects.filter(a=a).filter(b=b)
Offizielle Dokumente: https://docs.djangoproject.com/de/dev/topics/db/queries/#spanning-multi-valued-relationships
In Verbindung stehender Beitrag: Verketten mehrerer Filter () in Django, ist dies ein Fehler?
Es gibt einen Unterschied, wenn Sie beispielsweise eine Anfrage an Ihr verwandtes Objekt haben
class Book(models.Model):
author = models.ForeignKey(Author)
name = models.ForeignKey(Region)
class Author(models.Model):
name = models.ForeignKey(Region)
Anfrage
Author.objects.filter(book_name='name1',book_name='name2')
Gibt einen leeren Satz zurück
und Anfrage
Author.objects.filter(book_name='name1').filter(book_name='name2')
Gibt Autoren zurück, die Bücher mit 'name1' und 'name2' haben.
Weitere Informationen finden Sie unter https://docs.djangoproject.com/de/dev/topics/db/queries/#s-spanning-multi-valued-relationships
Author.objects.filter(book_name='name1',book_name='name2')
ist nicht einmal gültige Python, es wäreSyntaxError: keyword argument repeated