Tenho um modelo parecido com este:
class Category(models.Model):
name = models.CharField(max_length=60)
class Item(models.Model):
name = models.CharField(max_length=60)
category = models.ForeignKey(Category)
Quero selecionar a contagem (apenas a contagem) de itens para cada categoria, portanto, no SQL seria tão simples quanto isto:
select category_id, count(id) from item group by category_id
Existe um equivalente a fazer isso "do jeito Django"? Ou o SQL simples é a única opção? Estou familiarizado com o método count () no Django, porém não vejo como group by se encaixaria lá.
Respostas:
Aqui, como acabei de descobrir, é como fazer isso com a API de agregação Django 1.1:
fonte
order_by()
se'category'
não for a ordem padrão. (Veja a resposta mais abrangente de Daniel.).annotate()
funciona de maneira um pouco diferente após.values()
: "No entanto, quando uma cláusula values () é usada para restringir as colunas que são retornadas no conjunto de resultados, o método para avaliar as anotações é um pouco diferente. Em vez de retornar uma anotação resultado para cada resultado no QuerySet original, os resultados originais são agrupados de acordo com as combinações exclusivas dos campos especificados na cláusula values (). "( Atualização : o suporte de agregação ORM completo agora está incluído no Django 1.1 . Fiel ao aviso abaixo sobre o uso de APIs privadas, o método documentado aqui não funciona mais nas versões pós-1.1 do Django. Eu não investiguei o porquê; se estiver no 1.1 ou posterior, você deve usar a API de agregação real de qualquer maneira.)
O suporte de agregação central já estava lá em 1.0; é apenas não documentado, sem suporte e ainda não tem uma API amigável além dele. Mas aqui está como você pode usá-lo mesmo assim até 1.1 chegar (por sua própria conta e risco, e com pleno conhecimento de que o atributo query.group_by não faz parte de uma API pública e pode mudar):
Se você iterar em query_set, cada valor retornado será um dicionário com uma chave de "categoria" e uma chave de "contagem".
Você não precisa ordenar por -count aqui, que está incluído apenas para demonstrar como é feito (tem que ser feito na chamada .extra (), não em outro lugar na cadeia de construção do queryset). Além disso, você também poderia dizer contagem (id) em vez de contagem (1), mas o último pode ser mais eficiente.
Note também que ao configurar .query.group_by, os valores devem ser nomes de colunas reais do banco de dados ('category_id') e não nomes de campos do Django ('category'). Isso ocorre porque você está ajustando a parte interna da consulta em um nível em que tudo está em termos de banco de dados, não em termos de Django.
fonte
Como eu estava um pouco confuso sobre como o agrupamento no Django 1.1 funciona, pensei em elaborar aqui como exatamente você o usará. Primeiro, para repetir o que Michael disse:
Observe também que você precisa
from django.db.models import Count
!Isso selecionará apenas as categorias e, em seguida, adicionará uma anotação chamada
category__count
. Dependendo da ordem padrão, isso pode ser tudo de que você precisa, mas se a ordem padrão usar um campo diferentecategory
deste não funcionará . A razão para isso é que os campos obrigatórios para o pedido também são selecionados e tornam cada linha única, de modo que você não agrupará os itens como deseja. Uma maneira rápida de corrigir isso é redefinir o pedido:Isso deve produzir exatamente os resultados desejados. Para definir o nome da anotação, você pode usar:
Então você terá uma anotação chamada
mycount
nos resultados.Tudo o mais sobre agrupamento era muito direto para mim. Certifique-se de verificar a API de agregação do Django para informações mais detalhadas.
fonte
Como é isso? (Diferente de lento.)
Tem a vantagem de ser curto, mesmo que busque muitas linhas.
Editar.
A versão de uma consulta. BTW, isso geralmente é mais rápido do que SELECT COUNT (*) no banco de dados. Experimente para ver.
fonte