J'ai un modèle qui ressemble à ceci:
class Category(models.Model):
name = models.CharField(max_length=60)
class Item(models.Model):
name = models.CharField(max_length=60)
category = models.ForeignKey(Category)
Je veux sélectionner le nombre (juste le nombre) d'éléments pour chaque catégorie, donc en SQL, ce serait aussi simple que ceci:
select category_id, count(id) from item group by category_id
Y a-t-il un équivalent de faire cela "à la manière Django"? Ou le SQL brut est-il la seule option? Je connais la méthode count () de Django, mais je ne vois pas comment group by s'y adapterait.
Réponses:
Voici, comme je viens de le découvrir, comment faire cela avec l'API d'agrégation Django 1.1:
la source
order_by()
if'category'
n'est pas la commande par défaut. (Voir la réponse plus complète de Daniel.).annotate()
fonctionne légèrement différemment après un.values()
: "Cependant, lorsqu'une clause values () est utilisée pour contraindre les colonnes renvoyées dans le jeu de résultats, la méthode d'évaluation des annotations est légèrement différente. Au lieu de renvoyer un annoté résultat pour chaque résultat du QuerySet d'origine, les résultats d'origine sont regroupés en fonction des combinaisons uniques des champs spécifiés dans la clause values (). "( Mise à jour : la prise en charge complète de l'agrégation ORM est désormais incluse dans Django 1.1 . Fidèle à l'avertissement ci-dessous concernant l'utilisation des API privées, la méthode documentée ici ne fonctionne plus dans les versions post-1.1 de Django. Je n'ai pas cherché à comprendre pourquoi; si vous êtes sur 1.1 ou version ultérieure, vous devez quand même utiliser la véritable API d'agrégation .)
Le support d'agrégation de base était déjà présent dans la version 1.0; il est simplement non documenté, non pris en charge et n'a pas encore d'API conviviale en plus. Mais voici comment vous pouvez quand même l'utiliser jusqu'à l'arrivée de la version 1.1 (à vos risques et périls, et en sachant parfaitement que l'attribut query.group_by ne fait pas partie d'une API publique et pourrait changer):
Si vous parcourez ensuite query_set, chaque valeur renvoyée sera un dictionnaire avec une clé "category" et une clé "count".
Vous n'êtes pas obligé de classer par -count ici, c'est juste inclus pour montrer comment cela se fait (cela doit être fait dans l'appel .extra (), pas ailleurs dans la chaîne de construction du jeu de requêtes). De plus, vous pouvez tout aussi bien dire count (id) au lieu de count (1), mais ce dernier peut être plus efficace.
Notez également que lors de la définition de .query.group_by, les valeurs doivent être des noms de colonne DB réels ('category_id') et non des noms de champs Django ('category'). C'est parce que vous peaufinez les internes de la requête à un niveau où tout est en termes de base de données, pas en termes de Django.
la source
Étant donné que j'étais un peu confus sur le fonctionnement du regroupement dans Django 1.1, j'ai pensé expliquer ici comment vous l'utilisez exactement. Tout d'abord, pour répéter ce que Michael a dit:
Notez également que vous devez
from django.db.models import Count
!Cela sélectionnera uniquement les catégories, puis ajoutera une annotation appelée
category__count
. Selon la commande par défaut, cela peut être tout ce dont vous avez besoin, mais si la commande par défaut utilise un champ autre quecategory
celui-ci ne fonctionnera pas . La raison en est que les champs requis pour la commande sont également sélectionnés et rendent chaque ligne unique, de sorte que vous ne serez pas regroupé comme vous le souhaitez. Un moyen rapide de résoudre ce problème consiste à réinitialiser la commande:Cela devrait produire exactement les résultats que vous souhaitez. Pour définir le nom de l'annotation, vous pouvez utiliser:
Ensuite, vous aurez une annotation appelée
mycount
dans les résultats.Tout le reste concernant le regroupement était très simple pour moi. N'oubliez pas de consulter l' API d'agrégation Django pour des informations plus détaillées.
la source
Comment c'est? (Autre que lent.)
Il a l'avantage d'être court, même s'il récupère beaucoup de lignes.
Éditer.
La version à une requête. BTW, c'est souvent plus rapide que SELECT COUNT (*) dans la base de données. Essayez-le pour voir.
la source