Angenommen, ich habe die folgenden Modelle
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=50)
In einer Ansicht habe ich eine Liste mit aktiven Filtern, die als Kategorien bezeichnet werden . Ich möchte Fotoobjekte filtern, bei denen alle Tags in Kategorien vorhanden sind .
Ich habe es versucht:
Photo.objects.filter(tags__name__in=categories)
Dies entspricht jedoch jedem Element in Kategorien, nicht allen Elementen.
Wenn also Kategorien ['Urlaub', 'Sommer'] wären, möchte ich Fotos mit einem Feiertags- und einem Sommer-Tag.
Kann das erreicht werden?
python
django
filter
django-queryset
Sander van Leeuwen
quelle
quelle
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
ist der richtige Weg. (Dies ist das gleiche wie im Beispiel von jpic). Jederfilter
sollteJOIN
der Abfrage weitere s hinzufügen , damit Sie einen Annotationsansatz wählen können, wenn zu viele vorhanden sind.Antworten:
Zusammenfassung:
Eine Option ist, wie von jpic und sgallen in den Kommentaren vorgeschlagen,
.filter()
für jede Kategorie hinzuzufügen . Jede weiterefilter
fügt weitere Verknüpfungen hinzu, was für kleine Kategorien kein Problem sein sollte.Es ist die Aggregation Ansatz . Diese Abfrage wäre für eine große Anzahl von Kategorien kürzer und möglicherweise schneller.
Sie haben auch die Möglichkeit, benutzerdefinierte Abfragen zu verwenden .
Einige Beispiele
Versuchsaufbau:
Verwenden des Ansatzes für verkettete Filter :
Resultierende Abfrage:
Beachten Sie, dass jeder der Abfrage
filter
mehr hinzufügtJOINS
.Mit Anmerkung Ansatz :
Resultierende Abfrage:
AND
edQ
Objekte würden nicht funktionieren:Resultierende Abfrage:
quelle
t3
t2
t3
Photo.objects.filter(tags__in=tags)
Stimmt mit Fotos überein, die eines der Tags haben, nicht nur mit Fotos, die alle haben. Einige von denen, die nur eines der gewünschten Tags haben, haben möglicherweise genau die Anzahl der Tags, nach denen Sie suchen, und einige von denen, die alle gewünschten Tags haben, haben möglicherweise auch zusätzliche Tags.Ein anderer Ansatz, der funktioniert, obwohl nur PostgreSQL, verwendet
django.contrib.postgres.fields.ArrayField
:Beispiel aus Dokumenten kopiert :
ArrayField
verfügt über einige leistungsstärkere Funktionen wie Überlappungs- und Indextransformationen .quelle
Dies kann auch durch dynamische Abfragegenerierung mit Django ORM und etwas Python-Magie erfolgen :)
Die Idee ist, für jede Kategorie geeignete Q-Objekte zu generieren und diese dann mit dem AND-Operator zu einem QuerySet zu kombinieren. Zum Beispiel für Ihr Beispiel wäre es gleich
quelle
filter
wäre die gleiche wieand
für Q-Objekte in einem Filter ... Mein Fehler.filter
zu wechselnexclude
und einen Negationsoperator verwenden. Wie so:res = Photo.exclude(~reduce(and_, [Q(tags__name=c) for c in categories]))
Ich verwende eine kleine Funktion, die Filter über eine Liste für einen bestimmten Operator und einen Spaltennamen iteriert:
und diese Funktion kann so aufgerufen werden:
Es funktioniert auch mit jeder Klasse und mehr Tags in der Liste. Operatoren können alle sein wie 'iexact', 'in', 'enthält', 'ne', ...
quelle
quelle
Wenn wir es dynamisch machen wollen, folgen Sie dem Beispiel:
quelle