Django aggregation in templates?

2019-03-06 14:18发布

问题:

I'm thinking a bit about the concept of Django aggregates. I don't quite "get" how they can be used in my case. Basically i have a three-tier hierarchy of objects in my model, and the lowest object (Bar) contain values I want to aggregate.

class Bar(models.Model):
    amount = models.FloatField()

class Foo(models.Model):
    bars = models.ManyToManyField(Bar)

class MyUser(models.Model):
    foos = models.ManyToManyField(Foo)

I want my template to do the equivalent of this:

{% for user in users %}
    <h2>{{ user }}</h2>
    <table>
        <tr>
            <td>Foo</td><
            <td>Average bar amount</td>
        </tr>
    {% for foo in user.foos.all %}
        <tr>
            <td>{{ foo }}</td>
            <td>{{ foo.bars.all.aggregate(Avg('amount')) }}
        </tr>
    {% endfor %}
    </table>
{% endfor %}

But of course this template is just pseudo code, it won't work because, I guess, templates are not supposed to do this by design. But on the other hand, having to pre-calculate the average amounts in my view would feel wrong too. How should this problem be approached?

I'm still new to Django so I want to know the "Django way" of doing this :)

回答1:

This work should be done in the view. You say that feels wrong but that's exactly what the view is meant to do: retrieve the data that is going to be presented to the user. Take a look at the Django FAQ: http://docs.djangoproject.com/en/dev/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names

Django appears to be a MVC framework, but you call the Controller the “view”, and the View the “template”. How come you don’t use the standard names?

In our interpretation of MVC, the “view” describes the data that gets presented to the user. It’s not necessarily how the data looks, but which data is presented. The view describes which data you see, not how you see it. It’s a subtle distinction.

A side point to this is that based on how you are using the average in your template it would probably make more sense to use annotate rather than aggregate. http://docs.djangoproject.com/en/1.1/ref/models/querysets/#annotate-args-kwargs



回答2:

The templates do not let you call functions that take arguments. See here for what they let you do.

The canonical way to deal with this would be to either add some helper methods/managers to your models or to create a custom template tag or filter.