2009-09-29 6 views
0

J'ai des modèles comme celui-ci:Évite O (n) des requêtes avec Django

class PledgeItem(models.Model): 
    title = models.CharField(...) 
    usd_amount = models.DecimalField(...) 

class Pledger(models.Model): 
    name = models.CharField(...) 
    ... 

class Pledge(models.Model): 
    pledger = models.ForeignKey(Pledger) 
    item = models.ForeignKey(PledgeItem) 
    usd_amount = models.DecimalField(...) 
    ... 

Mon PledgeItem a une méthode de travail quel est le pourcentage de celui-ci est engagé (par exemple, un élément pourrait coûter 100 $, et ont 3 annonces de contributions de 20 $ chacun, ce qui signifie qu'il est de 60% promis):

class PledgeItem(models.Model): 
    ... 
    def percentage_pledged(self): 
     pledge_total = Pledge.objects.filter(item = self).sum(usd_amount) 
     return (pledge_total/self.usd_amount) * 100 

aux fins de cette question, s'il vous plaît suppose que je gérer correctement self.usd_amount étant nul, et le cas où il n'y a pas Pledges sur le PledgeItem (bien que Je dois demander, pourquoi sum(field) renvoie None dans ces cas?).

Le problème est, si j'appelle percentage_pledged dans une liste de nPledgeItems, j'ai une requête par PledgeItem. Existe-t-il un moyen élégant de résoudre ce problème sans utiliser les signaux save pour mettre à jour un champ percentage_pledged? Ce serait bien si je pouvais pré-extraire ces données d'une manière ou d'une autre (c'est-à-dire aller chercher tous les Pledges en une fois, puis les parcourir). Je ne suis pas sûr de savoir à quoi ressemblerait une solution (par exemple, où cet ensemble de Pledges vivrait-il?), Mais je suis sûr que c'est un problème courant (et qui m'a déjà ennuyé), alors j'ai pensé voir comment les gens plus expérimentés avec Django l'ont résolu. Peut-être que les signaux save appartiennent à cette catégorie, en particulier pour les sites de type "faible écriture, lecture haute".

Répondre

3

Ceci est un travail pour les nouvelles fonctionnalités d'agrégation dans Django 1.1.

Vous souhaitez 'annoter' un champ pledge_sum pour chaque PledgeItem d'un ensemble de requêtes. Cela se fait facilement:

from django.db.models import Sum 
PledgeItems.objects.all().annotate(pledge_sum=Sum(pledge__usdamount)) 

Il est évident que vous pouvez remplacer all() avec tout ce que les filtres que vous voulez.

Vous devrez toujours effectuer le calcul du pourcentage sur chaque PledgeItem, mais cela ne générera aucune requête supplémentaire.

+0

Fonctionne un charme, génial! –

Questions connexes