2010-10-04 34 views
58

Sono curioso di sapere se c'è qualche modo per fare una query in Django che non sia un "SELECT * FROM..." sotto. Sto cercando di fare un "SELECT DISTINCT columnName FROM ..." invece.Seleziona DISTINCT colonne individuali in django?

In particolare ho un modello che assomiglia a:

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

dove il Rank è un rango all'interno di un Category. Mi piacerebbe essere in grado di scorrere tutte le categorie facendo qualche operazione su ogni posizione all'interno di quella categoria.

Vorrei prima ottenere un elenco di tutte le categorie nel sistema e quindi eseguire una query per tutti i prodotti in quella categoria e ripetere finché tutte le categorie sono state elaborate.

Preferisco evitare l'SQL grezzo, ma se devo andare lì, andrebbe bene. Anche se non ho mai codificato SQL raw in Django/Python prima.

risposta

122

Un modo per ottenere l'elenco dei nomi delle colonne distinte dal database è quello di utilizzare in combinazione con distinct()values().

Nel tuo caso è possibile effettuare le seguenti operazioni per ottenere i nomi delle categorie distinte:

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" 

Ci sono un paio di cose da ricordare. Innanzitutto, restituirà un ValuesQuerySet che si comporta in modo diverso da QuerySet. Quando accedi, il primo elemento di q (sopra) riceverai un dizionario, NON un'istanza di ProductOrder.

In secondo luogo, sarebbe una buona idea leggere lo warning note nei documenti sull'utilizzo di distinct(). L'esempio sopra funzionerà ma tutte le combinazioni di distinct() e values() non possono.

PS: è una buona idea usare inferiore nomi in per i campi in un modello. Nel tuo caso questo significherebbe riscrivere il modello come illustrato di seguito:

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

Il metodo descritto di seguito è ora disponibile in django 1.4 ed è bello se hai bisogno di un'istanza ProductOrder con distinzione sensibile ai campi ;-) –

25

È piuttosto semplice in realtà se si utilizza PostgreSQL, è sufficiente utilizzare distinct(columns).

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

Si noti che questa funzione è stata inclusa in Django dal 1,4

+2

Doc: http://docs.djangoproject.com/en/dev/ref/models/querysets/#distinct – kafuchau

+8

-1: 'distinct' non si assume alcuna campi come argomento, la documentazione dimostra che e controlla anche il codice: http://code.djangoproject.com/browser/django/trunk/django/db/models/query.py#L646 –

+2

Lazerscience è corretto. 'Distinct' non accetta nomi di colonne come argomenti. –

10

Le altre risposte vanno bene, ma questo è un po 'più pulito, nel senso che dà solo i valori, come si otterrebbe da una query DISTINCT, senza qualsiasi cruft di Django.

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

o

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

E, funziona senza PostgreSQL.

Questo è meno efficiente dell'utilizzo di a.distinto(), presumendo che DISTINCT nel tuo database sia più veloce di un python set, ma è ottimo per nudellare attorno alla shell.

+0

'values_list' non inserisce' DISTINCT' nella query sql, quindi questo porterebbe più valori se ci fossero . – mehmet

+1

no non lo è - questo è ciò che 'set' è per –

+0

non l'ho visto :) – mehmet

7

L'ordine utente con quel campo e quindi distinto.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()