Wählen Sie UNTERSCHIEDLICHE einzelne Spalten in Django?


93

Ich bin gespannt, ob es in Django eine Möglichkeit gibt, eine Abfrage durchzuführen, die kein " SELECT * FROM..." darunter ist. Ich versuche SELECT DISTINCT columnName FROM ...stattdessen ein " " zu machen.

Insbesondere habe ich ein Modell, das aussieht wie:

class ProductOrder(models.Model):
   Product  = models.CharField(max_length=20, promary_key=True)
   Category = models.CharField(max_length=30)
   Rank = models.IntegerField()

wo das Rankist ein Rang innerhalb eines Category. Ich möchte in der Lage sein, alle Kategorien zu durchlaufen, wobei für jeden Rang innerhalb dieser Kategorie eine Operation ausgeführt wird.

Ich möchte zuerst eine Liste aller Kategorien im System erhalten und dann alle Produkte in dieser Kategorie abfragen und wiederholen, bis jede Kategorie verarbeitet ist.

Ich würde lieber rohes SQL vermeiden, aber wenn ich dorthin muss, wäre das in Ordnung. Obwohl ich noch nie zuvor Raw SQL in Django / Python codiert habe.

Antworten:


185

Eine Möglichkeit, die Liste der verschiedenen Spaltennamen aus der Datenbank abzurufen, ist die Verwendung distinct() in Verbindung mit values().

In Ihrem Fall können Sie Folgendes tun, um die Namen verschiedener Kategorien abzurufen:

q = ProductOrder.objects.values('Category').distinct()
print q.query # See for yourself.

# The query would look something like
# SELECT DISTINCT "app_productorder"."category" FROM "app_productorder"

Hier gibt es ein paar Dinge zu beachten. Erstens wird a zurückgegeben, ValuesQuerySetdas sich anders verhält als a QuerySet. Wenn Sie beispielsweise auf das erste Element von q(oben) zugreifen, erhalten Sie ein Wörterbuch , NICHT eine Instanz von ProductOrder.

Zweitens wäre es eine gute Idee, den Warnhinweis in den Dokumenten über die Verwendung zu lesen distinct(). Das obige Beispiel funktioniert, aber alle Kombinationen von distinct()und values()möglicherweise nicht.

PS : Es ist eine gute Idee, Kleinbuchstaben für Felder in einem Modell zu verwenden. In Ihrem Fall würde dies bedeuten, dass Sie Ihr Modell wie folgt umschreiben:

class ProductOrder(models.Model):
    product  = models.CharField(max_length=20, primary_key=True)
    category = models.CharField(max_length=30)
    rank = models.IntegerField()

1
Die unten beschriebene Methode ist jetzt in Django 1.4 verfügbar und ist hilfreich, wenn Sie eine ProductOrder-Instanz mit feldabhängiger Unterscheidung benötigen ;-)
Jonathan Liuti

60

Es ist eigentlich ganz einfach, wenn Sie PostgreSQL verwenden , verwenden Sie einfach distinct(columns)( Dokumentation ).

Productorder.objects.all().distinct('category')

Beachten Sie, dass diese Funktion seit 1.4 in Django enthalten ist


@lazerscience, @Manoj Govindan: Es tut mir leid, Sie haben Recht. Es scheint, dass ich Django gepatcht habe, um diese Funktion hinzuzufügen. Ich habe einen Link zum Patch hinzugefügt
Wolph

3
Dies ist jetzt in Django SVN und wird in Django 1.4
Will Hardy

14
Hinweis: Wenn Sie PostgreSQL nicht verwenden, können Sie kein eindeutiges () Argument angeben. Halten Sie sich am besten an die oben akzeptierte Lösung.
Mark Chackerian

In den Tests can_distinct_on_fieldsscheint dies nur Postgres zu sein
Skylar Saveland

3
plus 1, ist aber all()hier nicht notwendig
Antony Hatchkins

17

Die anderen Antworten sind in Ordnung, aber dies ist insofern etwas sauberer, als es nur die Werte angibt, die Sie von einer DISTINCT-Abfrage erhalten würden, ohne dass Crj von Django verwendet wird.

>>> set(ProductOrder.objects.values_list('category', flat=True))
{u'category1', u'category2', u'category3', u'category4'}

oder

>>> list(set(ProductOrder.objects.values_list('category', flat=True)))
[u'category1', u'category2', u'category3', u'category4']

Und es funktioniert ohne PostgreSQL.

Dies ist weniger effizient als die Verwendung von .distinct (), vorausgesetzt, DISTINCT in Ihrer Datenbank ist schneller als eine Python set, eignet sich jedoch hervorragend zum Nudeln in der Shell.


values_listwird nicht DISTINCTin die SQL-Abfrage eingefügt, daher würde dies mehrere Werte bringen, wenn dies der Fall wäre.
Mehmet

12

Benutzer bestellen mit diesem Feld und dann unterscheiden.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.