2009-02-11 2 views
3

Я борюсь с дизайном приложения django. Учитывая следующие модели:Показать несколько связанных моделей Django в проблеме проектирования таблиц

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) 

Я хотел бы вид/шаблон, который отображает таблицу HTML, которая показывает в первом столбце все А объекты, во втором столбце все объекты B (сгруппированных по А), на которые ссылаются на A и в последнем столбце - сумма всех значений val из объектов C, которые относятся к каждому B. Все это с суммой для каждого объекта A. Следующий пример показывает, что я ищу:

 
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] 

Может кто-нибудь дать мне совет, как разработать такую ​​проблему (к сожалению, мой код часто заканчивается в довольно беспорядок)?

Должен ли я расширять классы моделей с помощью соответствующих методов? Выполняет ли пользовательский запрос для всех табличных данных в представлении? Просто получить все объекты через менеджеров и сделать большинство вещей в шаблоне?

Спасибо за каждый ответ.

С уважением,

Luke.

ответ

5

Если у вас есть такая модель дат, поэтому вы можете получить все b s, а затем группировать их по a и рассчитать общую сумму. Код может выглядеть следующим образом:

Вид:

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}) 

И шаблон:

{% 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:

Для совместим с Django 1.0 вы должны заменить annotate вызов extra select или in-python.

  1. С подзапроса:

    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 - имя приложения

  2. Это в-питон расчета:

    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))) 
    #... 
    

    Но она производит N дополнительных запросов (где N = len(bs))

+0

Спасибо, что выглядит намного чище по сравнению с тем, что я придумал, но это будет работать только с Django v1.1 и более поздними версиями, правильно? – Lukas

+0

Да, он совместим с текущими магистральными и будущими версиями из-за агрегации. Вы можете заменить его 'extra' select (subquery) или вычислить суммы' C.val' для каждого 'B' в python –

+0

Lukas, посмотрите на отредактированный ответ –

1

Отказ от ответственности: Я новичок себя Джанго, но если я понял его основные понятия права, то:

Учитывая, что вид вы хотите - как вы говорите - просто представление основных данных , то вы должны выполнить сортировку и группировку в модуле просмотра. Я не думаю, что вы должны возиться с моделью (это всего лишь данные), ни с шаблоном (который является всего лишь макетом представления).

Смежные вопросы