Эквивалент Джанго для подсчета и группы по
у меня есть модель, которая выглядит так:
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, однако я не вижу, как группы по происходит.
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