E
E
Egor Kazantsev2015-04-03 21:23:00
Django
Egor Kazantsev, 2015-04-03 21:23:00

How to speed up a Django template?

There is this template:

{% for o1 in o11 %}
<span>{{o1.n}}</span>
{% for o2 in o22 %}
 {% if o2.o1 == o1 %}
<span>{{ o2t.n}}</span>
{% for t in tt %}
{% if t.o == o2 %}
 <a href="/{{ t.u }}/">{{ t.n }}</a><span>({{ t.c }})</span>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}

o1 contains a dozen objects
o2 hundreds
o3 thousands
Essentially 3 levels of nesting. Everything is fine - but this template is rendered in 3 seconds =( You remove this piece - less than half a second. Tell me how to optimize it and what to avoid in django templates

Answer the question

In order to leave comments, you need to log in

3 answer(s)
Y
Yuri Shikanov, 2015-04-03
@dizballanze

1. Cache
2. Switch to jinja2

K
kazmiruk, 2015-04-03
@kazmiruk

No matter how you twist it, you will have 1 million iterations, which is a lot. From more or less real options - take this code out of the template (generate a flat list in the controller, and then pass it to the template). Those. as a result you should have something like:

{% for el in l %}
<span>{{o1.n}}</span>
{% if el.flag1 %}
...
{% if el.flag2 %}
...
{% endif %}
{% endif %}
{% endfor %}

Although templates compile, they are generally slower than code. After you get a method that generates such a list, cache the list (or you can even cache a piece of the template with the list). Apparently, this is something like breadcrumbs of categories and should not change often. Since rendering in 3 seconds is hell, when flushing the cache, this list should be immediately placed back so as not to make the user wait. Those. should be something like this: generated a new list, atomically replaced the old list with a new one. Perhaps there are other options for optimizing the code (for example, converting lists to dictionaries, eliminating repetitions in checks, etc.)
You can also pervert wrappers - do not do a full enumeration, but make a method that will apply a binary search to the list, for example. And you will get not O(N), but O(log2N). But besides here it is necessary to look applicability to your code.

A
Alexey Cheremisin, 2015-04-04
@leahch

O!!! It is necessary to make convolution of objects!

from collections import defaultdict

_t_by_o2 = defaultdict(list)
for t in tt:
   _t_by_o2[t.o].append=t

_o2_by_o1 = defaultdict(list)
for o2 in o22:
   _o2_by_o1[o2.o1].append=o2

Now in the template you can work with hashes only in the o11 loop without ifs
{% for o1 in o11 %}
<span>{{o1.n}}</span>
{% for o2 in _o2_by_o1.get(o1,[]) %}
<span>{{ o2t.n}}</span>
{% for t in _t_by_o2.get(o2,[]) %}
 <a href="/{{ t.u }}/">{{ t.n }}</a><span>({{ t.c }})</span>
{% endfor %}
{% endfor %}
{% endfor %}

Something like this!
PS. I don’t understand at all why such wild cycles and what are they caused by?! In the browser, we still won’t display a lemon of objects. This means that there are only a maximum of a hundred or two of them, and if so, then something is wrong in the conservatory of relationships, or we are trying to do their processing in the wrong place. You may wonder why we need to bind these objects when displayed and why they are still in such a nightmare ?!

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question