Ответ 1
Здесь, как я только что узнал, как это сделать с API агрегации Django 1.1:
from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))
У меня есть модель, которая выглядит так:
class Category(models.Model):
name = models.CharField(max_length=60)
class Item(models.Model):
name = models.CharField(max_length=60)
category = models.ForeignKey(Category)
Я хочу выбрать count (только количество) элементов для каждой категории, поэтому в SQL это будет так просто:
select category_id, count(id) from item group by category_id
Есть ли эквивалент этого "пути Django"? Или простой SQL - единственный вариант? Я знаком с методом count() в Django, но я не вижу, как будет соответствовать группа.
Здесь, как я только что узнал, как это сделать с API агрегации Django 1.1:
from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))
(Обновление). Полная поддержка агрегации ORM теперь включена в Django 1.1. Верно нижеследующее предупреждение об использовании private APIs, описанный здесь метод больше не работает в версиях Django после версии 1.1. Я не понял, почему: если вы на 1.1 или более поздней версии, вы должны использовать реальный API агрегации в любом случае.)
Поддержка базовой агрегирования уже была в 1.0; он просто недокументирован, неподдерживается и еще не имеет дружественного API. Но вот как вы можете использовать его в любом случае до тех пор, пока не прибудет 1.1 (на свой страх и риск и в полной мере осознавая, что атрибут query.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, каждое возвращаемое значение будет являться словарем с ключом "категория" и "счетчиком".
Вам не нужно заказывать по -count здесь, это просто включено, чтобы продемонстрировать, как это сделать (это нужно сделать в вызове .extra(), а не в другом месте в цепочке построения запроса). Кроме того, вы можете просто сказать count (id) вместо count (1), но последнее может быть более эффективным.
Обратите также внимание, что при установке .query.group_by значения должны быть фактическими именами столбцов DB ('category_id'), а не именами полей Django ('category'). Это связано с тем, что вы настраиваете внутренности запроса на уровне, где все в терминах БД, а не в терминах 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
, это не будет работать. Причина этого в том, что поля, требуемые для упорядочения, также выбираются и делают каждую строку уникальной, поэтому вы не будете группировать материал так, как хотите. Один быстрый способ исправить это - reset упорядочение:
Item.objects.values('category').annotate(Count('category')).order_by()
Это даст точно результаты, которые вы хотите. Чтобы задать имя аннотации, вы можете использовать:
...annotate(mycount = Count('category'))...
Затем вы получите аннотацию под названием mycount
в результатах.
Все остальное о группировке было для меня очень простым. Для получения более подробной информации ознакомьтесь с API агрегации Django.
Как это? (За исключением медленных.)
counts= [ (c, Item.filter( category=c.id ).count()) for c in Category.objects.all() ]
Преимущество состоит в том, чтобы быть коротким, даже если он извлекает много строк.
Изменить.
Версия одного запроса. Кстати, это часто быстрее, чем SELECT COUNT (*) в базе данных. Попробуйте это посмотреть.
counts = defaultdict(int)
for i in Item.objects.all():
counts[i.category] += 1