1

J'ai ce modèle:requête Django pour sommer les longueurs de ArrayFields

class Interaction(models.Model): 
    user = models.ForeignKey(User) 
    codes = ArrayField(models.CharField(choices=CODE_CHOICES)) 

et je suis en train de comprendre comment faire l'équivalent de cette requête SQL dans Django:

select user_id, sum(cardinality(codes)) from interaction group by user_id; 
  • J'ai essayé extra(select={codes_len':'cardinality(codes)'}), mais ne peut pas annotate ou aggregate sur un champ extra. J'ai essayé annotate(Sum("cardinality('codes')")), mais cardinality('codes') n'est pas un champ sur le modèle.
  • J'ai étudié l'écriture d'un champ agrégat personnalisé qui combinait Sum et cardinality, mais cela semblait ... fragile.
  • J'ai découvert dans les documents que __len ne fonctionne pas correctement sur un ArrayField, mais pas dans le contexte de annotate(Sum('codes__len')).
  • J'ai exclu le SQL brut car il y a beaucoup d'instructions WHERE (omises ici) qui rendent cette requête difficile à reconstruire à la main.

À ce stade, je pense que je n'ai pas d'autre choix que d'ajouter un champ au modèle qui est la longueur du champ codes et le désordre avec save() pour le synchroniser.

N'y a-t-il vraiment aucun autre moyen? Est-ce que je manque quelque chose?

Répondre

1

Il s'avère que la fonction d'agrégat personnalisé est la solution!

avec ce qui suit:

from django.db.models import Aggregate 

class SumCardinality(Aggregate): 
    template = 'SUM(CARDINALITY(%(expressions)s))' 

La requête est aussi simple que:

Interaction.objects().filter(xxx).\ 
    values('user_id').annotate(codes_len=SumCardinality('codes'))