2010-10-04 2 views
58

Мне любопытно, есть ли способ сделать запрос в Django, который не является «SELECT * FROM...» снизу. Вместо этого я пытаюсь сделать «SELECT DISTINCT columnName FROM ...».Выберите DISTINCT отдельных столбцов в django?

В частности у меня есть модель, которая выглядит следующим образом:

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

где Rank это звание в пределах Category. Я хотел бы иметь возможность перебирать все категории, выполняющие некоторые операции над каждым рангом в этой категории.

Сначала я хотел бы получить список всех категорий в системе, а затем запросить все продукты этой категории и повторить до тех пор, пока не будет обработана каждая категория.

Я бы предпочел избежать необработанного SQL, но если мне нужно идти туда, все будет хорошо. Хотя я никогда раньше не кодировал необработанный SQL в Django/Python.

ответ

122

Один способ получить список различных имен столбцов из базы данных является использование distinct() в сочетании с values().

В вашем случае вы можете сделать следующее, чтобы получить имена различных категорий:

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" 

Есть несколько вещей, чтобы помнить здесь. Во-первых, это вернет ValuesQuerySet, который ведет себя иначе, чем QuerySet. Когда вы скажете, первый элемент q (см. Выше) вы получите словарь, а не экземпляр ProductOrder.

Во-вторых, было бы неплохо прочитать warning note в документах об использовании distinct(). Вышеприведенный пример будет работать, но все комбинации distinct() и values() могут не совпадать.

PS: это хорошая идея использовать ниже регистр имен для полей в модели. В вашем случае это будет означать, переписав свою модель, как показано ниже:

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

Метод, описанный ниже, теперь доступен в django 1.4 и хорош, если вам нужен экземпляр ProductOrder с полями, отличными от поля ;-) –

25

Это довольно просто на самом деле , если вы используете PostgreSQL, просто используйте distinct(columns).

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

Обратите внимание, что эта функция была включена в Django 1.4, так как

+2

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

+8

-1: 'distinct' не принимает каких-либо полей в качестве аргумента, документация показывает, что а также проверить код: http://code.djangoproject.com/browser/django/trunk/django/db/models/query.py#L646 –

+2

Lazerscience верен. 'Distinct' не будет принимать имена столбцов в качестве аргументов. –

10

Другие ответы прекрасны, но это немного чище, в том, что она дает только значения, как вы получили бы от DISTINCT запроса, без любой рывок из Django.

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

или

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

И это работает без PostgreSQL.

Это менее эффективно, чем использование.distinct(), предполагая, что DISTINCT в вашей базе данных быстрее, чем python set, но он отлично подходит для noodling вокруг оболочки.

+0

'values_list' не помещает' DISTINCT' в sql-запрос, поэтому это может привести к нескольким значениям, если бы они были , – mehmet

+1

нет, это не так - это 'set' для –

+0

я этого не видел :) – mehmet

7

Пользователь заказывает с этим полем, а затем делает отчетливым.

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