2009-02-11 11 views
3

Je suis aux prises avec la conception d'une application django. Étant donné les modèles suivants:Afficher plusieurs modèles Django liés dans un problème de conception de table

class A(models.Model): 
    name = models.CharField(max_length=255) 

class B(models.Model): 
    name = models.CharField(max_length=255) 
    a = models.ForeignKey(A) 

class C(models.Model): 
    val = models.IntegerField() 
    b = models.ForeignKey(B) 

Je voudrais une vue/modèle qui affiche un tableau HTML qui montre dans la première colonne tous les A objets, dans la deuxième colonne tous les objets B (regroupés par A) qui sont référencés à A et dans la dernière colonne une somme de tous les val de C objets qui sont référencés à chaque B. Tout cela avec une somme pour chaque objet A. L'exemple suivant montre ce que je cherche:

 
A1.name | B1.name [where FK to A1] | sum(C.val) [where FK to B1] 
A1.name | B2.name [where FK to A1] | sum(C.val) [where FK to B2] 
A1.name |     Total | sum(C.val) [where FK to Bx (all B that have FK to A1] 
A2.name | B3.name [where FK to A2] | sum(C.val) [where FK to B3] 
A2.name |     Total | sum(C.val) [where FK to Bx (all B that have FK to A2] 

Quelqu'un peut-il me donner un conseil comment concevoir un tel problème (malheureusement, mon code se termine souvent dans tout un gâchis)?

Dois-je étendre les classes du modèle avec des méthodes appropriées? Faire une requête personnalisée pour l'ensemble des données de la table dans la vue? Juste obtenir tous les objets via les gestionnaires et faire la plupart des choses dans le modèle?

Merci pour chaque réponse.

Cordialement,

Luke.

Répondre

5

Si vous avez ce modèle de date, vous pouvez récupérer tous les b s, puis les regrouper par a et calculer le montant total. Le code peut ressembler à ceci:

Vue:

from django.utils.itercompat import groupby 

def view(request): 
    bs = B.objects.all().annotate(sum_of_c_val=Sum('c.val'))\ 
         .select_related('a') 
    b_by_a = [ 
      { 
       'grouper': key, 
       'list': list(val), 
       'total': sum([b.sum_of_c_val for b in val]) 
      } 
      for key, val in 
      groupby(bs, lambda b: b.a) 
    ] 

    return render_to_response('tmpl.html', {'b_by_a': b_by_a}) 

et modèle:

{% for b_group in b_by_a %} 
    {% for b in b_group.list %} 
    <tr> 
     <td>{{b_group.grouper.name}}</td> 
     <td>{{b.name}}</td> 
     <td>{{b.sum_of_c_val}}</td> 
    </tr> 
    {% endfor %} 
    <tr> 
     <td>{{b_group.grouper.name}}</td> 
     <td>Total</td> 
     <td>{{b_group.total}}</td> 
    </tr> 
{% endfor %} 

EDIT:

Pour compatible avec Django 1.0, vous devez remplacer annotate appel avec extra sélectionnez ou calcul de montant in-python.

  1. Avec sous-requête:

    bs = B.objects.all().extra(
        select={ 
         'sum_of_c_val': 'SELECT SUM(app_c.val) FROM app_c WHERE app_c.b_id=app_b.id' 
        }).select_related('a') 
    #... 
    

    app - nom de l'application

  2. Ce calcul en python:

    bs = B.objects.all().select_related('a') 
    for b in bs: 
        b.sum_of_c_val = sum(map(int, b.c.all().values_list("val", flat=True))) 
    #... 
    

    Mais il produit N requêtes supplémentaires (où N = len(bs))

+0

Merci, cela semble beaucoup plus propre par rapport à ce que j'ai trouvé, mais cela ne fonctionnera qu'avec Django v1.1 et plus tard, correct? – Lukas

+0

Oui, il est compatible avec le tronc actuel et les versions futures en raison de l'agrégation. Vous pouvez le remplacer par 'extra' select (sous-requête) ou calculer les montants' C.val' pour chaque 'B' en python –

+0

Lukas, regardez ma réponse éditée –

1

Disclaimer: Je suis un Django moi débutant, mais si je comprends ses concepts de base à droite, puis:

Étant donné que la vue que vous voulez est - comme vous le dites - juste une vue des données sous-jacente , alors vous devriez faire le tri et le regroupement dans le module de vue. Je ne pense pas que vous devriez jouer avec le modèle (qui est juste les données), ni avec le modèle (qui est juste une mise en page de la vue).

Questions connexes