J'utilise un modèle de transaction pour suivre tous les événements traversant le système
class Transaction(models.Model):
actor = models.ForeignKey(User, related_name="actor")
acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
action_id = models.IntegerField()
......
comment obtenir les 5 meilleurs acteurs de mon système?
Dans SQL, ce sera essentiellement
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
django
django-queryset
totoromeow
la source
la source
Réponses:
Selon la documentation, vous devez utiliser:
values (): spécifie quelles colonnes vont être utilisées pour "grouper par"
Documents Django:
annotate (): spécifie une opération sur les valeurs groupées
Documents Django:
L'ordre par clause est explicite.
Pour résumer: vous groupez par, générant un jeu de requêtes d'auteurs, ajoutez l'annotation (cela ajoutera un champ supplémentaire aux valeurs renvoyées) et enfin, vous les classez par cette valeur
Reportez-vous à https://docs.djangoproject.com/en/dev/topics/db/aggregation/ pour plus d'informations
Bon à noter: si vous utilisez Count, la valeur passée à Count n'affecte pas l'agrégation, juste le nom donné à la valeur finale. L'agrégateur regroupe par combinaisons uniques des valeurs (comme mentionné ci-dessus) et non par la valeur transmise à Count. Les requêtes suivantes sont les mêmes:
la source
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
, n'oubliez pas d'importer Count depuis django.db.models. MerciCount
(et peut-être d'autres agrégateurs), la valeur transmise àCount
n'affecte pas l'agrégation, juste le nom donné à la valeur finale. L'agrégateur regroupe par combinaisons uniques devalues
(comme mentionné ci-dessus), et non par valeur transmise àCount
.total
le nom est-il donné et le nombre utilisé dans sql estCOUNT('actor')
ce qui dans ce cas n'a pas d'importance, mais si par exemplevalues('x', 'y').annotate(count=Count('x'))
, vous obtiendrezCOUNT(x)
, pasCOUNT(*)
ouCOUNT(x, y)
, simplement essayé./manage.py shell
Tout comme @Alvaro a répondu à l'équivalent direct de Django pour la
GROUP BY
déclaration:se fait par l'utilisation de
values()
et desannotate()
méthodes suivantes:Cependant, une dernière chose doit être soulignée:
Si le modèle a un ordre par défaut défini dans
class Meta
, la.order_by()
clause est obligatoire pour des résultats corrects. Vous ne pouvez tout simplement pas l'ignorer même si aucune commande n'est prévue.De plus, pour un code de haute qualité, il est conseillé de toujours mettre une
.order_by()
clause aprèsannotate()
, même s'il n'y en a pasclass Meta: ordering
. Une telle approche rendra la déclaration à l'épreuve du temps: elle fonctionnera comme prévu, indépendamment des modifications futures apportées àclass Meta: ordering
.Laissez-moi vous donner un exemple. Si le modèle avait:
Alors une telle approche NE fonctionnera PAS:
C'est parce que Django effectue des performances supplémentaires
GROUP BY
sur chaque champ declass Meta: ordering
Si vous imprimez la requête:
Il sera clair que l'agrégation ne fonctionnera PAS comme prévu et par conséquent, la
.order_by()
clause doit être utilisée pour effacer ce comportement et obtenir des résultats d'agrégation appropriés.Voir: Interaction avec la commande par défaut ou order_by () dans la documentation officielle de Django.
la source
.order_by()
m'a sauvé deordering
Meta.