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

Мне любопытно, есть ли способ сделать запрос в 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.


person jamida    schedule 04.10.2010    source источник


Ответы (4)


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

Во-вторых, было бы неплохо прочитать предупреждение в документации об использовании 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()
person Manoj Govindan    schedule 04.10.2010
comment
Описанный ниже метод теперь доступен в django 1.4 и хорош, если вам нужен экземпляр ProductOrder с отдельным полем ;-) - person Jonathan Liuti; 16.10.2012
comment
Я хочу знать, как изменить этот код, чтобы он работал и с операторами where. Как 2_ ? - person Prakash Dahal; 30.04.2021
comment
Как бы вы преобразовали это в обычный набор запросов? - person Josh; 13.05.2021

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

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

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

person Wolph    schedule 04.10.2010
comment
@lazerscience, @ Манодж Говиндан: Прости, ты прав. Кажется, я пропатчил Django, чтобы добавить эту функцию. Я добавил ссылку на патч - person Wolph; 04.10.2010
comment
Теперь это в Django SVN и будет в Django 1.4. - person Will Hardy; 28.12.2011
comment
Примечание: если вы не используете PostgreSQL, вы не можете указать аргумент unique (). Лучше всего придерживаться принятого выше решения. - person Mark Chackerian; 05.02.2013
comment
в тестах это can_distinct_on_fields, который, похоже, предназначен только для Postgres - person Skylar Saveland; 18.02.2013
comment
Было бы полезно, если бы те, кто проголосовал против, могли объяснить свои отрицательные голоса, исходный ответ был действительно неверным, но это было исправлено около 4 лет назад;) - person Wolph; 09.04.2014
comment
плюс 1, но all() здесь не нужен - person Antony Hatchkins; 23.04.2015

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

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

or

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

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

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

person Mark Chackerian    schedule 02.02.2015
comment
values_list не помещает DISTINCT в запрос sql, поэтому это принесло бы несколько значений, если бы они были. - person mehmet; 30.06.2016
comment
Это ужасная идея с точки зрения производительности! - person boatcoder; 20.11.2020
comment
да - не делайте этого в продакшене, если у вас не крошечный стол! - person Mark Chackerian; 20.11.2020

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

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
person SuperNova    schedule 15.10.2015