2010-04-17 11 views
6

Ich versuche eine effiziente Möglichkeit zu finden, den Rang eines Objekts in der Datenbank zu finden, die sich auf seine Punktzahl bezieht. Meine naive Lösung sieht wie folgt aus:Wie bekomme ich die Position eines Ergebnisses in der Liste nach einem order_by?

rank = 0 
for q in Model.objects.all().order_by('score'): 
    if q.name == 'searching_for_this' 
    return rank 
    rank += 1 

Es sollte möglich sein, die Datenbank zu erhalten, die Filterung zu tun, mit order_by:..

Model.objects.all() order_by ('Score') Filter (name = 'searching_for_this')

Es scheint jedoch keine Möglichkeit zu geben, den Index für den order_by-Schritt nach dem Filter abzurufen.

Gibt es einen besseren Weg, dies zu tun? (Mit Python/Django und/oder Raw-SQL.)

Mein nächster Gedanke ist es, Ränge auf Einsatz vorberechnen, aber das scheint chaotisch.

+0

etwas wie: 1. 'wählen Punktzahl als retrieved_score where name = 'searching_for_this'' 2.' select count (*), wo Score <= retrieved_score' -> Rang – jfs

Antwort

7

Ich glaube nicht, dass Sie dies in einer Datenbankabfrage mit Django ORM tun können. Aber wenn es Sie nicht stört, würde ich eine benutzerdefinierte Methode auf ein Modell erstellen:

from django.db.models import Count 

class Model(models.Model): 
    score = models.IntegerField() 
    ... 

    def ranking(self): 
     aggregate = Model.objects.filter(score__lt=self.score).aggregate(ranking=Count('score')) 
     return aggregate['ranking'] + 1 

Sie können dann mit überall „Ranking“, als ob es ein normales Feld war:

print Model.objects.get(pk=1).ranking 
+0

Danke, genau das habe ich gesucht. –

+0

Danke. Beachten Sie, dass in dem Beispiel gab, ist die Reihenfolge (in anderen Worten „je niedriger die Punktzahl, desto höher das Ranking“) wurde aufsteigend, so ging ich mit ihm. Wenn der höhere Punktestand das Ranking erhöhen sollte, sollten Sie "score__lt" in "score__gt" ändern. –

+2

Dies sollte rechnerisch teuer sein, da es die Ranking Aggregatabfrage für jede Zeile in 'Model.objects.all()' – mehmet

2

In "Raw SQL" mit einer standardkonformen Datenbank-Engine (PostgreSql, SQL Server, Oracle, DB2, ...) können Sie einfach den SQL-Standard RANK Funktion verwenden - aber das wird nicht in populären aber unterstützt Nicht-Standard-Engines wie MySql und Sqlite, und (vielleicht gerade deshalb) "Django" diese Funktionalität für die Anwendung nicht "Oberfläche".

+0

Ah, ich bin Tests auf SQLite vor der Bereitstellung zu PGSQL. –

+2

@BobBob, wenn Sie möchten, könnten Sie eine kleine lokale Instanz von PostgreSQL für Testzwecke ausführen - es ist weder schwer noch lästig. –

1

Verwendung so etwas wie diese:

obj = Model.objects.get(name='searching_for_this') 
rank = Model.objects.filter(score__gt=obj.score).count() 

Sie können es im Voraus berechnen Reihen und speichern Modell, wenn sie häufig verwendet werden und die Leistung auswirken.