11
votes

Filtrer l'agrégat dans le Django Orm

J'ai une fonction qui ressemble à ceci: xxx

Je veux seulement compter les postes qui ont leur statut marqué comme "actif". Y a-t-il un moyen simple d'ajouter un filtre avant la fonction de comptage?

Définitions de modèle: xxx


1 commentaires

Comment ressemblent vos modèles?


5 Réponses :


0
votes

Oui. Fais-le. Cela devrait fonctionner comme prévu:

self.thread_set.filter(active_status=1).aggregate(num_posts=Count('post'))['num_posts']


3 commentaires

Je suis à peu près sûr que filtrerait le jeu de threads et non les poteaux dans chaque fil.


Whoops, tu as raison. Vous auriez besoin d'obtenir une liste complète des messages d'un fil, puis de filtrer les messages actifs, puis d'agréger ceux-ci. Mais les autres choses à propos d'avoir agrégat () dernier reste toujours. Je vais fouiller puis modifier ma réponse.


Je n'ai pas encore de code de code, mais cela ressemble à ce que vous recherchez est-ce: docs.djangoproject.com/fr/dev/topics/db/agregistration/... (jointures et agrégats)



0
votes

J'ai regardé quelque chose de similaire et j'ai trouvé une excellente solution. J'utilise quelque chose comme ceci:

def post_count(self):
        return len(Post.objects.filter(someModel = self).filter(active_status = 1))


4 commentaires

Pourquoi pas post.Object.filter (Somemodel = Self, Active_Status = 1) .Count ()?


Aussi, j'utilise un agrégat ici car je veux plus que le nombre de post compter sur un thread particulier (représenté par Somemodel dans votre exemple), j'ai besoin du nombre de post pour tous les threads dans une catégorie, c'est pourquoi j'utilise Thread_Set.


Ouais, l'exemple que j'ai donné est assez bâclé. Votre code est définitivement plus propre. Le point que j'essayais de faire est que je suis à peu près sûr que vous devez travailler à travers le modèle de poste et non le modèle de fil.


Si un travail sur le modèle de poste et que j'ai manuellement résumer tous les threads d'une catégorie. Donc, si une catégorie comportait 1000 threads, il faudrait au moins ces nombreuses requêtes pour la DB. De toute évidence, cela ne serait jamais acceptable et je ferais simplement un SQL cru à la place.



0
votes

Vous voudrez peut-être regarder l'écriture d'un objet de gestionnaire personnalisé:

http://docs.djangoproject.com/fr/1.1/ Thèmes / DB / Gestionnaires /

Je n'ai pas utilisé agrégat () , mais cela peut vous permettre d'écrire un gestionnaire personnalisé pour fournir un filtré_thread_set , puis effectuez self.active_thread_set. agrégat (...) . Sinon, cela vous permettra de faire le SQL personnalisé et d'ajouter un Num_poststs sur les objets thread (voir le pollmanager.with_counts () exemple .)


0 commentaires

13
votes

OK, maintenant que la question inclut les définitions de modèle, je vous soumettais que cela devrait fonctionner, à moins que votre version de Django ne supporte aucune fonctionnalité que j'utilise ici (auquel cas, s'il vous plaît faites le moi savoir!): xxx pré>

django permet aux filtres __ dans code> de prendre un query pour décider de la clause in code> doit ressembler à SQL, donc si vous passez thread__in = thread_set code>, Django filtrera les messages afin que seuls ceux dont le champ code> pointe code> pointe sur l'un des ID code> S des threads de votre thread_set code> reste pour le Agrégation code> Appel à voir. p>

Ceci devrait filtrer les messages avec une seule requête DB forte> avec quelque chose comme Où thref_id in ... code> à l'intérieur, plutôt qu'avec une requête par fil, ce qui serait effectivement horrible. Si quelque chose d'autre est arrivé, ce serait un bug dans django ... p>

Le résultat doit être au plus deux requêtes pour établir une catégorie (code> s postcount fort > - Un pour obtenir thread_set code> et un autre pour compter les messages. L'alternative consiste à faire une jointure de thread / post pour être filtrée en fonction de la catégorie code> 'S code>' S code> champ code> et post code> s Statut Le champ, que je ne m'attendrais pas nécessairement à être beaucoup plus rapide. (je dis 'au plus', parce que je suppose qu'ils pourraient être fusionnés automatiquement ... même si je ne pense pas que cela se produirait avec Django actuel. Je ne peux pas vérifier ATM, désolé.) Strike> p>

EDIT: strong> Référence de l'API de requête de Django dit ceci sur __ dans code> filtres: p>


dans strong> p> blockQquote>

dans une liste donnée. p>

Exemple: P>

inner_q = Blog.objects.filter(name__contains='Cheddar').values('pk').query
entries = Entry.objects.filter(blog__in=inner_q)


2 commentaires

C'est la meilleure solution jusqu'à présent. Il peut également être le meilleur que vous puissiez faire avec le django orm. J'espérais juste qu'il y avait un moyen facile d'ajouter un filtre à l'original Self.Thread_SeRead.Accrégation (Num_posts = compter ('Post')) ['Num_post s'].


Eh bien, je me suis finalement arrondi pour regarder les documents sur __ pouce de filtres avec des querySets intérieurs ... on dirait qu'ils devraient jouer assez bien, non? :-) Dommage, je ne peux pas sembler trouver un moyen de brancher facilement un filtre dans votre code, car je peux voir comment cela pourrait être un ajustement plus naturel à une manière particulière de penser à ce que dit le code ... ( Bien que, d'autre part, vous comptez des postes, alors passez à travers la poste me semble correct.) J'espère que cela fonctionne pour votre performance-sage, en tout cas. Tout le meilleur!



0
votes

serait bien de changer les choses un peu?

Comme illustré ci-dessous, vous pouvez ajouter une propriété post_count à la classe de thread, qui compte des messages actifs dans un fil. P>

Ce post_count pourrait puis être utilisé pour calculer des messages actifs dans une catégorie en ajoutant tous les messages actifs dans tout le thread dans une catégorie. P>

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(max_length=100, blank=True, primary_key=True)
    ordering = models.IntegerField(max_length=3, default=0)

    @property
    def thread_count(self):
        return self.thread_set.all().count()

    @property
    def post_count(self): # <-- Changed
        return reduce(lambda x,y: x + y, [x.post_count for x in self.thread_set.all()])

class Thread(models.Model):
    user = models.ForeignKey(User)
    category = models.ForeignKey(Category)
    title = models.CharField(max_length=100)
    slug = models.SlugField(max_length=100)
    content = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    latest_activity = models.DateTimeField(auto_now_add=True)

    @property
    def post_count(self): # <---- Newly added
        return self.post_set.filter(status = 'ACTIVE').count()

class Post(models.Model):
    thread = models.ForeignKey(Thread)
    parent = models.ForeignKey('Post', null=True, blank=True)
    display_name = models.CharField(max_length=100)
    email = models.EmailField(db_index=True)
    ip_address = models.IPAddressField(null=True, blank=True)
    content = models.TextField()
    status = models.CharField(choices=STATUS_CHOICES, max_length=25, db_index=True, default='approved')
    created = models.DateTimeField()


0 commentaires