views:

63

answers:

6

One very annoying thing with strict MVC is that you can make the smallest processing in template. While it's usually a good practice, in this case it gets in the way:

  • I make a for loop on a queryset of a lot of objects and display some attributes;
  • Attributes are properties, making heavy processing (I don't want to trigger them twice).
  • At the end I need to display a total of the value of these attributes.

I now I have to do the sum once in the view, then reloop (and call the properties again) to display. I can improve that by loopring once in the view and caching the results, but then I get more code just to bypass this limitations, and I use more memory.

At this stage of the project, it's not THAT a big deal. Performance is not the issue yet at all. But I really like to know is there is a solution for this.

My best bet is to write a template tag that does the job, but it's such a chore :-) Do you know somebody that would have done that already?

E.G:

Ideal, it sould be something like:

{% for object in object_list %}
    - {% sum_in_and_print object.property total %}
{% endfor %}
total {{ total }}

that would display:

- 2
- 3
- 1
total 6
A: 

I'ts quite hard tag to write nicely. You will have to take care if evaluation order is ok (sum may be displayed above data). That you can have many sumup sections (maybe data too). You will need probably following structure:

{% raport %}
    {% sumup %}
        ...
    {% endsumup %}
    {% data %}
        ...
    {% enddata %}
    {% sumup %}
        ...
    {% endsumup %}
{% endraport %}

EDIT: Also you will have hard times, to parse multilevel structure:

{% raport %}
    {% sumup %}
        ...
    {% endsumup %}
    {% data %}
        {% raport %}
            {% sumup %}
                ...
            {% endsumup %}
            {% data %}
                ...
            {% enddata %}
            {% sumup %}
                ...
            {% endsumup %}
        {% endraport %}
    {% enddata %}
    {% sumup %}
        ...
    {% endsumup %}
{% endraport %}
Tomasz Wysocki
A: 

I don't know what project your're developing, but if you already require users to have JavaScript, you may check jQuery.calculation plugin. This is a great tool to shift all dumb calculations to the client side.

Michał Sałaban
This calculation should be able to work without javascipt. We believe in graceful degradation.
e-satis
A: 

Look into a different templating system like Mako. Mako especially allows full python code to run within the template.

Josh Smeaton
"Rewrite all your project in another template language" is not really the anwser you expect in a question saying 'I know I can write a tag to do that, but is there a shorter way ,or an existing tag?'.
e-satis
You don't need to re-write the entire project. You can use both templating languages at the same time, and just use the templating language with more (easier?) expressive power when the situation calls for it.
Josh Smeaton
Yet a bit overkill don't you think ? :-) I can't say I didn't play with the idea, but then it has maintenance and deployment issues, and there are other dev on the project, and so on.
e-satis
+1  A: 

I don't see how it's a chore. Check if the destination variable is in the context, and if not, initialize it with an empty-constructed object of the class of the first element in the list, then accumulate.

Ignacio Vazquez-Abrams
Not sure why this got voted down. This is how the tag would be written and seems pretty straightforward to me. Sometimes it's faster just to write the code than search for the snippet.
Jordan Reiter
A: 

Is there a compelling reason you're not doing this in the view?

What could be easier than:

total = sum([o.property for o in object_list])

Even if your goal is to use generics, you can always wrap it in a view function.

Let me add that I think the functionality you specifically want: to output a variable and add it to some arbitrary variable -- is such a unique one that absolutely you'll want to write your own template tag for it.

Fortunately, this is very easy.

Update

As I said in the question the property trigger HEAVY processing. I don't want to trigger them twice...

So you could try this instead:

rendered_object_list = []
for o in object_list:
    rendered_object_list.append({
        'field1': o.field1,
        'field2': o.field2,
        'property': o.property
    })
total = sum(o['property'] for o in rendered_object_list)

You process it once, in the view, then when you output it in the template, it's a dictionary, with everything in the dictionary already processed.

Jordan Reiter
As I said in the question the property trigger HEAVY processing. I don't want to trigger them twice: one for display, and once for summing. Otherwise, this is what I do of course.
e-satis
+1  A: 

As an alternative to calculating the sum in a view or template, what about QuerySet.Annotate(total_x=Sum('x')), to have the database take care of the calculations?

Aside: I believe trying to solve the actual problem sometimes warrants not answering the direct question when there is not an obvious answer.

Josh Smeaton
These are properties, not attributes. They can't be summed up on the db side.
e-satis