Ich versuche, die Suche nach einer Django-Site zu erstellen, die ich erstelle, und bei dieser Suche suche ich in 3 verschiedenen Modellen. Und um eine Paginierung in der Suchergebnisliste zu erhalten, möchte ich eine generische Objektlistenansicht verwenden, um die Ergebnisse anzuzeigen. Aber dazu muss ich 3 Abfragesätze zu einem zusammenführen.
Wie kann ich das machen? Ich habe das versucht:
result_list = []
page_list = Page.objects.filter(
Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term))
article_list = Article.objects.filter(
Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term) |
Q(tags__icontains=cleaned_search_term))
post_list = Post.objects.filter(
Q(title__icontains=cleaned_search_term) |
Q(body__icontains=cleaned_search_term) |
Q(tags__icontains=cleaned_search_term))
for x in page_list:
result_list.append(x)
for x in article_list:
result_list.append(x)
for x in post_list:
result_list.append(x)
return object_list(
request,
queryset=result_list,
template_object_name='result',
paginate_by=10,
extra_context={
'search_term': search_term},
template_name="search/result_list.html")
Das funktioniert aber nicht. Ich erhalte eine Fehlermeldung, wenn ich versuche, diese Liste in der allgemeinen Ansicht zu verwenden. In der Liste fehlt das Klonattribut.
Weiß jemand, wie ich die drei Listen zusammenführen kann page_list
, article_list
und post_list
?
django
search
django-queryset
django-q
espenhogbakk
quelle
quelle
union
.Antworten:
Das Verketten der Abfragesätze zu einer Liste ist der einfachste Ansatz. Wenn die Datenbank ohnehin für alle Abfragesätze getroffen wird (z. B. weil das Ergebnis sortiert werden muss), entstehen keine weiteren Kosten.
Die Verwendung
itertools.chain
ist schneller als das Schleifen jeder Liste und das Anhängen von Elementen nacheinander, da diesitertools
in C implementiert ist. Außerdem wird weniger Speicher benötigt als das Konvertieren jedes Abfragesatzes in eine Liste vor dem Verketten.Jetzt ist es möglich, die resultierende Liste zB nach Datum zu sortieren (wie im Kommentar von hasen j zu einer anderen Antwort gefordert). Die
sorted()
Funktion akzeptiert bequem einen Generator und gibt eine Liste zurück:Wenn Sie Python 2.4 oder höher verwenden, können Sie
attrgetter
anstelle eines Lambda verwenden. Ich erinnere mich, dass ich gelesen habe, dass es schneller ist, aber ich habe keinen merklichen Geschwindigkeitsunterschied für eine Million Artikel gesehen.quelle
from itertools import groupby
unique_results = [rows.next() for (key, rows) in groupby(result_list, key=lambda obj: obj.id)]
'list' object has no attribute 'complex_filter'
Versuche dies:
Es behält alle Funktionen der Abfragesätze bei, was nett ist, wenn Sie wollen
order_by
oder ähnlich.Bitte beachten Sie: Dies funktioniert nicht bei Abfragesätzen von zwei verschiedenen Modellen.
quelle
|
ist der Set Union Operator, nicht bitweises ODER.Zum Mischen von Abfragesätzen desselben Modells oder für ähnliche Felder einiger weniger Modelle steht ab Django 1.11 auch eine
qs.union()
Methode zur Verfügung:https://docs.djangoproject.com/de/1.11/ref/models/querysets/#django.db.models.query.QuerySet.union
quelle
Sie können die folgende
QuerySetChain
Klasse verwenden. Wenn Sie es mit Djangos Paginator verwenden, sollte es die Datenbank nur mitCOUNT(*)
Abfragen für alle Abfragesätze undSELECT()
Abfragen nur für diejenigen Abfragesätze treffen, deren Datensätze auf der aktuellen Seite angezeigt werden.Beachten Sie, dass Sie angeben müssen,
template_name=
ob Sie aQuerySetChain
mit generischen Ansichten verwenden, auch wenn die verketteten Abfragesätze alle dasselbe Modell verwenden.In Ihrem Beispiel wäre die Verwendung:
Verwenden Sie dann
matches
den Paginator wieresult_list
in Ihrem Beispiel.Das
itertools
Modul wurde in Python 2.3 eingeführt, daher sollte es in allen Python-Versionen verfügbar sein, auf denen Django ausgeführt wird.quelle
Der große Nachteil Ihres aktuellen Ansatzes ist die Ineffizienz bei großen Suchergebnissen, da Sie jedes Mal die gesamte Ergebnismenge aus der Datenbank abrufen müssen, obwohl Sie nur eine Ergebnisseite anzeigen möchten.
Um nur die tatsächlich benötigten Objekte aus der Datenbank abzurufen, müssen Sie die Paginierung für ein QuerySet und nicht für eine Liste verwenden. Wenn Sie dies tun, schneidet Django das QuerySet tatsächlich in Scheiben, bevor die Abfrage ausgeführt wird, sodass die SQL-Abfrage OFFSET und LIMIT verwendet, um nur die Datensätze abzurufen, die Sie tatsächlich anzeigen. Sie können dies jedoch nur tun, wenn Sie Ihre Suche irgendwie in eine einzelne Abfrage packen können.
Warum nicht die Modellvererbung verwenden , da alle drei Modelle Titel- und Textfelder haben ? Lassen Sie einfach alle drei Modelle von einem gemeinsamen Vorfahren mit Titel und Text erben und führen Sie die Suche als einzelne Abfrage für das Vorfahrenmodell durch.
quelle
Wenn Sie viele Abfragesätze verketten möchten, versuchen Sie Folgendes:
Dabei gilt: docs ist eine Liste von Abfragesätzen
quelle
Zitiert von https://groups.google.com/forum/#!topic/django-users/6wUNuJa4jVw . Siehe Alex Gaynor
quelle
Dies kann auch auf zwei Arten erreicht werden.
1. Weg dies zu tun
Verwenden Sie den Union-Operator für das Abfrageset
|
, um die Vereinigung von zwei Abfragesätzen zu übernehmen. Wenn beide Abfragesätze zu demselben Modell / Einzelmodell gehören, können Abfragesätze mithilfe des Union-Operators kombiniert werden.Zum Beispiel
2. Weg dies zu tun
Eine andere Möglichkeit, eine Kombinationsoperation zwischen zwei Abfragesätzen zu erreichen, ist die Verwendung der itertools- Kettenfunktion.
quelle
Anforderungen :
Django==2.0.2
,django-querysetsequence==0.8
Wenn Sie kombinieren
querysets
und trotzdem mit aQuerySet
herauskommen möchten , sollten Sie sich die Django-Queryset-Sequenz ansehen .Aber eine Anmerkung dazu. Es braucht nur zwei
querysets
als Argument. Aber mit Python könnenreduce
Sie es immer auf mehrerequeryset
s anwenden .Und das ist es. Unten ist eine Situation, in die ich geraten bin und wie ich sie angestellt habe
list comprehension
,reduce
unddjango-queryset-sequence
quelle
Book.objects.filter(owner__mentor=mentor)
nicht das Gleiche? Ich bin nicht sicher, ob dies ein gültiger Anwendungsfall ist. Ich denke, ein mussBook
möglicherweise mehrereowner
s haben, bevor Sie anfangen müssen, so etwas zu tun.Hier ist eine Idee ... ziehen Sie einfach eine ganze Seite mit Ergebnissen aus jedem der drei heraus und werfen Sie dann die 20 am wenigsten nützlichen heraus ... dies eliminiert die großen Abfragesätze und auf diese Weise opfern Sie nur ein wenig Leistung statt viel
quelle
Dies erledigt die Arbeit ohne Verwendung anderer Bibliotheken
quelle
Diese rekursive Funktion verkettet ein Array von Abfragesätzen zu einem Abfragesatz.
quelle