Die Modelle
Hier ist die Grundmodelle Setup haben wir.Filterung Django QuerySet basierend auf ManyToMany Beziehung Existenz
Eine Liste enthält viele Elemente, und ein Element kann in vielen Listen enthalten sein. Für einen gegebenen Artikel, wenn irgendeiner seiner Listen gut ist (d. H. list.bad == False
), dann ist der Artikel gut. Wenn ein Artikel in keiner guten Listen erscheint, dann ist es schlecht.
Wir haben ein benutzerdefiniertes QuerySet für Items, mit einer Methode, nur gute Items zurückzugeben, und einer Methode, nur fehlerhafte Items zurückzugeben.
class Item(models.Model):
objects = ItemQuerySet.as_manager()
name = models.CharField(max_length=255, unique=True)
class List(models.Model):
name = models.CharField(max_length=255, unique=True)
bad = models.BooleanField(default=True)
items = models.ManyToManyField(Item, related_name='lists')
class ItemQuerySet(models.QuerySet):
def bad(self):
return self.exclude(lists__bad=False)
def good(self):
return self.filter(lists__bad=False)
Das Szenario
Hier ist ein Beispiel für ein Szenario, das wir Probleme haben mit: einer schlechten List, eine gute Liste, und zwei Artikel.
BadList: GoodList:
- Item1 - Item1
- Item2
Da element1 in mindestens eine gute Liste erscheint, sollte es kommen in Item.objects.good()
, und nicht in Item.objects.bad()
.
Da Item2 in keiner guten Liste erscheint, sollte es in Item.objects.bad()
erscheinen und nicht in Item.objects.good()
.
Wir können das Szenario wie so ein:
# Create the two lists.
>>> goodlist = List.objects.create(name='goodlist', bad=False)
>>> badlist = List.objects.create(name='badlist', bad=True)
# Create the two items.
>>> item1 = Item.objects.create(name='item1')
>>> item2 = Item.objects.create(name='item2')
# Item1 goes in both lists
>>> goodlist.items.add(item1)
>>> badlist.items.add(item1)
# Item2 only in badlist
>>> badlist.items.add(item2)
Und in der Tat, Item.objects.good()
und Item.objects.bad()
Arbeit, wie wir erwarten:
>>> Item.objects.bad() # This returns what we want! Good!
<QuerySet [<Item: item2>]>
>>> Item.objects.good() # This returns what we want! Good!
<QuerySet [<Item: item1>]>
Das Problem
Dank für das mit mir tragen . Hier ist unser benutzerdefiniertes QuerySet falsch. Wenn wir auf die good()
und bad()
benutzerdefinierten QuerySet-Methoden bis Elemente einer einzelnen Liste zugreifen, erhalten wir falsche Ergebnisse.
>>> badlist.items.bad() # WRONG! We want to ONLY see item2 here!
<QuerySet [<Item: item1>, <Item: item2>]
>>> badlist.items.good() # WRONG! We want to see item1 here!
<QuerySet []>
Es scheint so, als wir badlist.items.bad()
tun, um die Abfrage nur hält badlist
bei der Bestimmung, ob die Gegenstände schlecht sind, anstatt alle Listen bedenkt, dass die Einzelteile sind in. Aber ich bin verwirrt, warum das wäre der Fall.
Mein Gedanke ist, dass in der ItemQuerySet.bad
Methode, ich möchte etwas wie self.exclude(any__lists__bad=False)
statt nur self.exclude(lists__bad=False)
. Aber natürlich existiert das Schlüsselwort any__
nicht wirklich, und ich bin nicht sicher, wie ich diese Logik in einem Django QuerySet korrekt ausdrücken kann. Es scheint, dass die Verwendung Q
Objekte der Weg nach vorne sein könnte, aber ich bin immer noch nicht ganz sicher, wie eine Abfrage wie folgt mit Q
Objekte auszudrücken.
In unserer tatsächlichen Datenbank gibt es weniger als 100 Listen, aber Millionen von Elementen. Aus Performance-Gründen ist es daher ideal, dies mit einer Abfrage zu tun und nicht mit einer Eigenschaft oder mehreren Abfragen.
Prost!
Danke für die Antwort! Das funktioniert zwar richtig, aber ich bin besorgt, dass andere Leute, die mit dem Code arbeiten, versuchen werden, die 'badlist.items.good()' Route zu benutzen, ohne es besser zu wissen. Ich möchte vermeiden, dass Menschen so in die Irre geführt werden. Darüber hinaus wollte ich dies im ursprünglichen Beitrag erwähnen: In unserer tatsächlichen Datenbank gibt es weniger als 100 Listen, aber Millionen von Items. Aus Performance-Gründen ist es daher ideal, dies mit einer Abfrage zu tun und nicht mit einer Eigenschaft oder mehreren Abfragen. –
Ah, hab's. Ich denke, Sie können dies mit der Anfrageset-Annotation tun. Ich habe meine Antwort oben mit einer Lösung bearbeitet, die meiner Meinung nach funktionieren sollte. Andernfalls würde ich zur Verbesserung der Leistung einfach die schlecht/gut Spalte zu Item hinzufügen und diese aktualisieren, was die Abfrage viel einfacher macht – Brobin