Ich habe ein Modell, das Bilder darstellt, die ich auf meiner Website präsentiere. Auf der Hauptwebseite möchte ich einige davon zeigen: die neueste, eine, die die meiste Zeit nicht besucht wurde, die beliebteste und eine zufällige.
Ich benutze Django 1.0.2.
Während die ersten drei mit Django-Modellen leicht zu ziehen sind, bereitet mir die letzte (zufällige) einige Probleme. Ich kann es aus meiner Sicht so codieren:
number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)
Es sieht nicht nach etwas aus, das ich aus meiner Sicht gerne hätte - dies ist vollständig Teil der Datenbankabstraktion und sollte im Modell enthalten sein. Außerdem muss ich mich hier um entfernte Datensätze kümmern (dann deckt die Anzahl aller Datensätze nicht alle möglichen Schlüsselwerte ab) und wahrscheinlich um viele andere Dinge.
Gibt es noch andere Möglichkeiten, wie ich das machen kann, vorzugsweise irgendwie innerhalb der Modellabstraktion?
quelle
Antworten:
Mit
order_by('?')
wird der Datenbankserver am zweiten Tag in der Produktion beendet. Ein besserer Weg ist so etwas wie das Abrufen einer zufälligen Zeile aus einer relationalen Datenbank .quelle
model.objects.aggregate(count=Count('id'))['count']
übermodel.objects.all().count()
.all()[randint(0, count - 1)]
. Vielleicht sollten Sie sich darauf konzentrieren, herauszufinden, welcher Teil der Antwort falsch oder schwach ist, anstatt "Off-by-One-Error" für uns neu zu definieren und die dummen Wähler anzuschreien. (Vielleicht ist es, dass es nicht verwendet.objects
?)Verwenden Sie einfach:
Es ist in der QuerySet-API dokumentiert .
quelle
random.choice(Model.objects.all())
?Die Lösungen mit order_by ('?') [: N] sind selbst für mittelgroße Tabellen extrem langsam, wenn Sie MySQL verwenden (Sie kennen keine anderen Datenbanken).
order_by('?')[:N]
wird in eineSELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N
Abfrage übersetzt .Dies bedeutet, dass für jede Zeile in der Tabelle die Funktion RAND () ausgeführt wird, dann die gesamte Tabelle nach dem Wert dieser Funktion sortiert wird und dann zuerst N Datensätze zurückgegeben werden. Wenn Ihre Tische klein sind, ist dies in Ordnung. In den meisten Fällen ist dies jedoch eine sehr langsame Abfrage.
Ich habe eine einfache Funktion geschrieben, die auch dann funktioniert, wenn IDs Löcher haben (einige Zeilen wurden gelöscht):
Es ist in fast allen Fällen schneller als order_by ('?').
quelle
Hier ist eine einfache Lösung:
quelle
Sie können einen Manager für Ihr Modell erstellen, um solche Aufgaben auszuführen. Um zuerst zu verstehen , was ein Manager ist, das
Painting.objects
ist ein Methode Manager, enthältall()
,filter()
,get()
etc. Ihren eigenen Manager erstellen , können Sie die Ergebnisse Filter vor und alle die gleichen Methoden haben, sowie Ihre eigenen Methoden, die Arbeit an den Ergebnissen .BEARBEITEN : Ich habe meinen Code geändert, um die
order_by['?']
Methode widerzuspiegeln . Beachten Sie, dass der Manager eine unbegrenzte Anzahl von Zufallsmodellen zurückgibt. Aus diesem Grund habe ich ein wenig Verwendungscode eingefügt, um zu zeigen, wie man nur ein einziges Modell erhält.Verwendung
Schließlich können Sie viele Manager in Ihren Modellen haben. Sie können also ein
LeastViewsManager()
oder erstellenMostPopularManager()
.quelle
Die anderen Antworten sind entweder potenziell langsam (verwenden
order_by('?')
) oder verwenden mehr als eine SQL-Abfrage. Hier ist eine Beispiellösung ohne Bestellung und mit nur einer Abfrage (unter der Annahme von Postgres):Beachten Sie, dass dies einen Indexfehler auslöst, wenn die Tabelle leer ist. Schreiben Sie sich eine modellunabhängige Hilfsfunktion, um dies zu überprüfen.
quelle
count()
im Voraus ausführen und auf die Rohabfrage verzichten.Nur eine einfache Idee, wie ich es mache:
quelle
Nur um einen (ziemlich häufigen) Sonderfall zu beachten: Wenn die Tabelle eine indizierte Spalte für das automatische Inkrementieren ohne Löschvorgänge enthält, ist eine Abfrage wie die optimale Methode für eine zufällige Auswahl:
das setzt eine solche Spalte mit dem Namen id für Tabelle voraus. In Django können Sie dies tun durch:
in dem Sie den Anwendungsnamen durch Ihren Anwendungsnamen ersetzen müssen.
Im Allgemeinen kann mit einer ID-Spalte order_by ('?') Viel schneller ausgeführt werden mit:
quelle
Dies wird dringend empfohlen. Es wird
eine zufällige Zeile aus einer relationalen DatenbankabgerufenWeil die Verwendung von django orm, um so etwas zu tun, Ihren Datenbankserver besonders dann verärgert, wenn Sie eine große Datentabelle haben: |
Und die Lösung besteht darin, einen Modellmanager bereitzustellen und die SQL-Abfrage von Hand zu schreiben;)
Update :
Eine andere Lösung, die auf jedem Datenbank-Backend funktioniert, auch auf nicht-rel-Lösungen, ohne benutzerdefinierte Informationen zu schreiben
ModelManager
. Zufällige Objekte aus einem Queryset in Django abrufenquelle
Möglicherweise möchten Sie denselben Ansatz verwenden , mit dem Sie einen Iterator testen, insbesondere wenn Sie mehrere Elemente testen möchten, um einen Beispielsatz zu erstellen . @MatijnPieters und @DzinX haben viel darüber nachgedacht:
quelle
OFFSET
), ist dies unnötig ineffizient.Ein viel einfacherer Ansatz besteht darin, einfach nach dem gewünschten Datensatz zu filtern und
random.sample
so viele auszuwählen, wie Sie möchten:Beachten Sie, dass Sie über Code verfügen sollten, um sicherzustellen, dass dieser
my_queryset
nicht leer ist.random.sample
Gibt zurück,ValueError: sample larger than population
wenn das erste Argument zu wenige Elemente enthält.quelle
Queryset
(zumindest mit Python 3.7 und Django 2.1); Sie müssen es zuerst in eine Liste konvertieren, die offensichtlich den gesamten Abfragesatz abruft.Hallo, ich musste einen zufälligen Datensatz aus einem Abfragesatz auswählen, dessen Länge ich auch melden musste (dh die Webseite produzierte das beschriebene Element und die Datensätze blieben übrig).
dauerte halb so lange (0,7s vs 1,7s) wie:
Ich vermute, es vermeidet, die gesamte Abfrage herunterzuziehen, bevor der zufällige Eintrag ausgewählt wird, und mein System reagiert auf eine Seite, auf die wiederholt zugegriffen wird, für eine sich wiederholende Aufgabe, bei der Benutzer den Countdown item_count sehen möchten.
quelle
Methode zum automatischen Inkrementieren des Primärschlüssels ohne Löschen
Wenn Sie eine Tabelle haben, in der der Primärschlüssel eine sequentielle Ganzzahl ohne Lücken ist, sollte die folgende Methode funktionieren:
Diese Methode ist viel effizienter als andere Methoden, die alle Zeilen der Tabelle durchlaufen. Es sind zwar zwei Datenbankabfragen erforderlich, beide sind jedoch trivial. Darüber hinaus ist es einfach und erfordert keine Definition zusätzlicher Klassen. Die Anwendbarkeit ist jedoch auf Tabellen mit einem automatisch inkrementierenden Primärschlüssel beschränkt, bei denen Zeilen nie gelöscht wurden, sodass keine Lücken in der Reihenfolge der IDs bestehen.
In dem Fall, in dem Zeilen so gelöscht wurden, dass es sich um Lücken handelt, kann diese Methode weiterhin funktionieren, wenn sie wiederholt wird, bis ein vorhandener Primärschlüssel zufällig ausgewählt wird.
Verweise
quelle
Ich habe eine sehr einfache Lösung, machen Sie einen benutzerdefinierten Manager:
und dann im Modell hinzufügen:
Jetzt können Sie es verwenden:
quelle
order_by('?').first()
mehr als 60 Mal.