Выбрать DISTINCT отдельные столбцы в django?

97

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

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

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.

Джамида
источник

Ответы:

196

Один из способов получить список различных имен столбцов из базы данных - использовать 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"

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

Во-вторых, было бы неплохо прочитать предупреждение в документации об использовании 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 с отдельным полем ;-)
Джонатан Лиути
66

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

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

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

Вольф
источник
@lazerscience, @ Манодж Говиндан: Мне очень жаль, ты прав. Кажется, я исправил Django, чтобы добавить эту функцию.
Добавил
3
Теперь он находится в Django SVN и будет в Django 1.4
Уилл Харди
14
Примечание: если вы не используете PostgreSQL, вы не можете указать аргумент unique (). Лучше всего придерживаться принятого выше решения.
Марк Чакериан
в тестах это, can_distinct_on_fieldsпохоже, только для Postgres
Skylar Saveland
4
плюс 1, но all()здесь не обязательно
Энтони Хэтчкинс
17

Другие ответы хороши, но это немного чище, поскольку дает только те значения, которые вы получили бы из запроса 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, но он отлично подходит для работы с оболочкой.

Марк Чакериан
источник
values_listне помещает DISTINCTв запрос sql, поэтому это принесло бы несколько значений, если бы они были.
мехмет
13

Пользователь упорядочивает по этому полю, а затем делает различное.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
Сверхновая звезда
источник