2009-06-29 9 views
4

Est-ce que entry_set doit être mis en cache avec select_related? Ma base de données reçoit toujours des appels même après avoir utilisé select_related. Les sections pertinentesdjango: select_related with entry_set

class Alias(models.Model): 
    achievements = models.ManyToManyField('Achievement', through='Achiever') 

    def points(self) : 
     points = 0 
     for a in self.achiever_set.all() : 
      points += a.achievement.points * a.count 
     return points 

class Achievement(models.Model): 
    name = models.CharField(max_length=100) 
    points = models.IntegerField(default=1) 

class Achiever(models.Model): 
    achievement = models.ForeignKey(Achievement) 
    alias = models.ForeignKey(Alias) 
    count = models.IntegerField(default=1) 

aliases = Alias.objects.all().select_related() 
for alias in aliases : 
    print "points : %s" % alias.points() 
    for a in alias.achiever_set.all()[:5] : 
     print "%s x %d" % (a.achievement.name, a.count) 

Et je vois une grande requête de jointure au début, puis des appels individuels pour chaque réalisation. Tant pour les points et pour la recherche de nom.

Est-ce un bug, ou est-ce que je fais quelque chose de mal?

Répondre

4

Select_related() ne fonctionne pas avec manytomanyfields. À l'heure actuelle, c'est quelque chose qui n'est pas planifié, mais qui pourrait être une caractéristique future. Voir http://code.djangoproject.com/ticket/6432

Dans ce cas, si vous voulez faire une seule requête vous avez deux options 1) Faites votre propre SQL, ne sera probablement pas joli ou rapide. 2) Vous pouvez également interroger sur le modèle avec la clé étrangère. Vous seriez en mesure d'utiliser select_related dans ce cas. Vous ne serez pas en mesure d'accéder à modelname_set mais avec un formatage, vous serez en mesure de vérifier les données dont vous avez besoin dans une seule requête. Aucune des options n'est idéale, mais vous pouvez l'obtenir à une vitesse égale.

+0

Bummer. Comment recommanderiez-vous que je fasse cette requête? SQL à la main? –

+5

Pour quiconque lisant, un équivalent 'select_related()' pour M2M et FK inverse est 'prefetch_related()' et a été ajouté en 1.4. – Bryan

+0

googletorp et @Brian, merci beaucoup. – Jamey

0

Dans Django 1.3 Vous pouvez utiliser Queryset.values ​​() et faire quelque chose comme:

Alias.objects[.filter().exclude() etc.].values('achievements__name', 'achievement__points') 

Only est drwaback que vous obtenez QuerySetList au lieu de QuerySet. Mais cela peut être simplement résolu en passant tous les champs nécessaires en valeurs() - Vous devez changer votre perception;)

Cela peut vous faire économiser quelques Dosen des requêtes ...

Les détails peuvent être trouvés ici dans django docs: http://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.values