Was ist der Unterschied zwischen Filter mit mehreren Argumenten und Kettenfilter in Django?
python
django
django-models
Testmobil
quelle
quelle
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.
quelle
(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 ... enthaltenentry.headline == 'Bill'
? .wollteentry__headline__contains='Lennon'
dieBill
Instanz nicht herausfiltern ?Der Fall, in dem sich die Ergebnisse von "Filterabfrage mit mehreren Argumenten" von "verketteter Filterabfrage" unterscheiden, ist folgender:
Beispiel:
Betrachten Sie meine Anwendung
my_company
hat zwei ModelleEmployee
undDependent
. Ein Mitarbeiter inmy_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 genommenEs
Employee
handelt sich also um ein Referenzmodell, auf das von mehr als dem ReferenzierungsmodellDependent
verwiesen werden kann. Betrachten Sie nun den Beziehungsstatus wie folgt: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:
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:
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
undprint 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.
quelle
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.
quelle
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 ,
quelle
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) >>>
quelle
Wenn Sie auf dieser Seite nach Möglichkeiten suchen, ein Django-Abfrageset mit mehreren Verkettungsfiltern dynamisch aufzubauen, die Filter jedoch vom
AND
Typ seinOR
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)
quelle
Diese Antwort basiert auf Django 3.1.
Umgebung
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>]>
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.
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'
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.
quelle
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?
quelle
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
quelle
Author.objects.filter(book_name='name1',book_name='name2')
ist nicht einmal gültige Python, es wäreSyntaxError: keyword argument repeated