Эквивалент Джанго для подсчета и группы по



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



class Category(models.Model):
name = models.CharField(max_length=60)

class Item(models.Model):
name = models.CharField(max_length=60)
category = models.ForeignKey(Category)


Я хочу выбрать количество (только количество) элементов для каждой категории, поэтому в SQL это было бы так просто:



select category_id, count(id) from item group by category_id


есть ли эквивалент этого "пути Джанго"? Или простой SQL-единственный вариант? Я знаком с count () метод в Django, однако я не вижу, как группы по происходит.

519   4  

4 ответов:

здесь, как я только что обнаружил, как это сделать с API агрегации Django 1.1:

from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))

(обновление: полная поддержка агрегации ORM теперь включена в Django 1.1. В соответствии с приведенным ниже предупреждением об использовании частных API, описанный здесь метод больше не работает в версиях Django после 1.1. Я не окопались, чтобы выяснить, почему; если вы находитесь на 1.1 или более поздней версии, вы должны использовать реальные агрегация API в любом случае.)

поддержка агрегации ядра уже была в 1.0; она просто недокументирована, не поддерживается и не имеет дружественный API на вершине его еще нет. Но вот как вы можете использовать его в любом случае, пока 1.1 не прибудет (на свой страх и риск, и в полной мере зная, что запрос.атрибут group_by не является частью публичного API и может измениться):

query_set = Item.objects.extra(select={'count': 'count(1)'}, 
                               order_by=['-count']).values('count', 'category')
query_set.query.group_by = ['category_id']

Если затем выполнить итерацию по query_set, каждое возвращаемое значение будет словарем с ключом "category" и ключом "count".

вам не нужно заказывать по счету здесь, это просто включено, чтобы продемонстрировать, как это делается (это должно быть сделано в .дополнительный() вызов, а не в другом месте в цепочке построения queryset). Кроме того, вы можете также сказать count(id) вместо count(1), но последний может быть более эффективным.

обратите внимание также, что при настройке .запрос.group_by, значения должны быть фактическими именами столбцов БД ('category_id'), а не именами полей Django ('category'). Это связано с тем, что вы настраиваете внутренние запросы на уровне, где все находится в терминах DB, а не в терминах Django.

поскольку я был немного смущен тем, как работает группировка в Django 1.1, я подумал, что здесь я расскажу, как именно Вы ее используете. Во-первых, повторить то, что сказал Майкл:

здесь, как я только что обнаружил, как это сделать с API агрегации Django 1.1:

from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))

Отметим также, что вам нужно from django.db.models import Count!

это позволит выбрать только категории, а затем добавить аннотацию под названием category__count. В зависимости от заказа по умолчанию это может быть все, что вам нужно, но если порядок по умолчанию использует поле, отличное от category это не будет работать. Причина этого заключается в том, что поля, необходимые для заказа, также выбраны и делают каждую строку уникальной, поэтому вы не получите материал, сгруппированный так, как вы хотите. Один быстрый способ исправить это-сбросить заказ:

Item.objects.values('category').annotate(Count('category')).order_by()

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

...annotate(mycount = Count('category'))...

тогда вы будете есть аннотация под названием mycount в результатах.

все остальное о группировке было очень просто для меня. Будьте уверены, чтобы проверить Django aggregation API для получения более подробной информации.

Как это? (Кроме медленного.)

counts= [ (c, Item.filter( category=c.id ).count()) for c in Category.objects.all() ]

Он имеет преимущество быть коротким, даже если он приносит много строк.


правка.

одна версия запроса. Кстати, это часто быстрее чем выбрать COUNT (*) в базе данных. Попробуй это увидеть.

counts = defaultdict(int)
for i in Item.objects.all():
    counts[i.category] += 1

Comments

    Ничего не найдено.