2010-10-04 37 views
58

Tengo curiosidad si hay alguna manera de hacer una consulta en Django que no sea un "SELECT * FROM..." debajo. Estoy tratando de hacer un "SELECT DISTINCT columnName FROM ..." en su lugar.Seleccione DISTINCT columnas individuales en django?

Específicamente tengo un modelo que se parece a:

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

donde el Rank es un rango dentro de un Category. Me gustaría poder iterar sobre todas las categorías realizando alguna operación en cada rango dentro de esa categoría.

Me gustaría obtener primero una lista de todas las categorías en el sistema y luego consultar para todos los productos en esa categoría y repetir hasta que se procese cada categoría.

Prefiero evitar SQL sin formato, pero si tengo que ir allí, estaría bien. Aunque nunca antes he codificado SQL sin formato en Django/Python.

Respuesta

122

Una forma de obtener la lista de nombres de columna distinta de la base de datos es utilizar distinct() conjuntamente con values().

En su caso, usted puede hacer lo siguiente para obtener los nombres de categorías distintas:

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" 

Hay un par de cosas a recordar aquí. Primero, esto devolverá un ValuesQuerySet que se comporta de manera diferente a QuerySet. Cuando acceda, por ejemplo, el primer elemento de q (arriba) obtendrá un diccionario, NO una instancia de ProductOrder.

En segundo lugar, sería una buena idea leer el warning note en los documentos sobre el uso de distinct(). El ejemplo anterior funcionará pero no todas las combinaciones de distinct() y values().

PS: es una buena idea usar menor nombres de los casos para los campos en un modelo. En su caso esto significaría volver a escribir el modelo como se muestra a continuación:

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

El método descrito a continuación ahora está disponible en django 1.4 y es bueno si necesita la instancia de ProductOrder con campo aware distinct ;-) –

25

Es bastante simple en realidad si está usando PostgreSQL, simplemente use distinct(columns).

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

Tenga en cuenta que esta característica se ha incluido en Django desde 1,4

+2

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

+8

-1: 'distinct' no tiene ningún campo como argumento, la documentación muestra que y también verifique el código: http://code.djangoproject.com/browser/django/trunk/django/db/models/query.py#L646 –

+2

Lazerscience es correcto. 'Distinct' no aceptará ningún nombre de columna como argumentos. –

10

Las otras respuestas están muy bien, pero esto es un poco más limpio, ya que sólo se indican los valores como la que se obtiene de una consulta DISTINCT, sin cualquier crumble de 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'] 

Y, funciona sin PostgreSQL.

Esto es menos eficiente que usar a.distinct(), suponiendo que DISTINCT en su base de datos es más rápido que un python set, pero es genial para andar con rodeos por el shell.

+0

'values_list' no pone' DISTINCT' en la consulta sql, por lo que esto traería múltiples valores si hubiera . – mehmet

+1

no, no - eso es lo que 'set' es para –

+0

no vi eso :) – mehmet

7

Por orden de usuario con ese campo, y luego hazlo distinto.

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