saintbyte
@saintbyte
Django developer

Как ускорить шаблон Django?

Есть вот такой шаблон:
{% 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 содержит десяток обьектов
o2 сотни
o3 тысячи

По сути 3 уровня вложенности. Все хорошо - но рендирится этот шаблон 3 секунды =( Убираешь этот кусок - все менее полсекунды. Подскажите как его оптимизировать и чего избегать в django шаблонах
  • Вопрос задан
  • 491 просмотр
Пригласить эксперта
Ответы на вопрос 3
dizballanze
@dizballanze
Software developer at Yandex
1. Кэшировать
2. Перейти на jinja2
Ответ написан
@kazmiruk
Как не крутите у Вас буде 1млн итераций, что много. Из более-менее реальных вариантов - вынести этот код из шаблона (генерировать плоский список в контроллере, а затем прокидывать его в шаблон). Т.е. в результате у вас должно быть что-то типа:
{% for el in l %}
<span>{{o1.n}}</span>
{% if el.flag1 %}
...
{% if el.flag2 %}
...
{% endif %}
{% endif %}
{% endfor %}

Хотя шаблоны и компилируются, но в целом выполняются медленнее, чем код. После того, как вы получите метод, генерирующий такой список - кешируйте список (или можно даже закешировать кусок шаблона со списком). Судя по всему это что-то типа хлебных крошек категорий и часто меняться не должен. Так как рендеринг в 3 секунды - ад, то при сбросе кеша этот список надо сразу помещать назад, чтобы не заставлять пользователя ждать. Т.е. должно быть как-то так: сгенерировали новый список, атомарно заменили старый список на новый. Возможно есть еще варианты оптимизации кода (например преобразование списков в словари, исключение повторений в проверках и т.п.)
Также можно извратиться обертками - не делать полный перебор, а сделать метод, который будет применять бинарный поиск к списку, к примеру. И Вы получите не O(N), а O(log2N). Но опять же тут надо смотреть применимость к Вашему коду.
Ответ написан
leahch
@leahch
Я мастер на все руки, я козлик Элек Мэк :-)
О!!! Нужно сделать свертку объектов!
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

Теперь в шаблоне можно работать с хешами только по циклу o11 без ифов
{% 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 %}

Как-то так!
PS. Я вообще не понимаю, зачем такие дикие циклы и чем они обусловлены?! В браузере мы всё равно лимон объектов не отобразим. Значит их всего максимум сотня-другая, а если это так, то что-то не то в консерватории взаимосвязей, или пытаемся делать не там их обработку. Может быть задуматься, почему нам нужно эти объекты связывать при отображении и почему они до сих пор находятся в таком кошмаре?!
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы