Django: Шаблоны. Наследование.

Прочитал статью «Фрагментарное кэширование в MVC веб-фреймворках». Статья описывает проблему кеширования фрагмета отображения, а именно проблему полного разделения контроллера и отображения - контроллер отрабатывает полностью до вызова отображения. Если в отображении мы кешируем фрагмент, это ничего не меняет - контроллер-то уже отработал! В статье описан способ этого избежать: сделать запрос данных "ленивым".

Начав писать, как это должно быть сделано правильно, решил написать, как устроены шаблоны Django, чтобы не-джанговодам тоже было понятно.

Как это сделано в Django?

Структура шаблонов Django

Управляющими элементами шаблонов Django являются переменные, фильтры и теги.

При рендеринге шаблона переменные заменяются на свое значение, вычисленное в контексте вызова. Синтаксис - двойные фигурные скобки - например: {{ title }}.

Фильтры служат для простых преобразований переменных. Синтаксис - переменная|имя_фильтра:"параметры". Фильтр может встречаться как в переменных, так и в качестве параметра тега. Например: {{ title|lowercase }}.

При рендеринге шаблона теги, грубо говоря, заменяются на результаты работы ассоциированной с этим тегом функции на питоне. Синтаксис: {% тег параметры %}, например: {% url blog_article slug=article.slug %}.

Программист может написать свои фильтры и теги, но об этом позже.

Кроме того, есть три специальных тега: include, block и extend. Тег include подставляет запрошенный шаблон (отрендеренный в текущем контексте).

Все выше перечисленное тривиально и в той или иной форме есть в любом движке шаблонов. Теперь перейдем к особенностям Django: на тегах block и extend строится наследование шаблонов. Остановимся на них подробнее.

Наследование шаблонов

Основная фишка шаблонов Django - наследование. Шаблон может расширять (уточнять) поведение родительского шаблона.

Любой участок шаблона может быть обернут в блочный тег (естественно, что тег не может начинаться перед, а заканчиваться внутри цикла). Блоку дается имя. Например:

{% block content %}
	тело блока
{% endblock %}

При помощи тега extend мы указываем, какой шаблон мы будем уточнять. Расширяя шаблон, мы можем переопредилить любые блоки, которые есть в родительском шаблоне. Все, что находится вне этих блоков, будет пропущено.

Получается мощный механизм, практически исключающий необходимость повторения частей шаблонов. Вкратце это описано в документации (см. ссылки в конце статьи). Давайте разберем реальный пример.

Пример наследования шаблонов

Допустим, мы хотим сделать сайт, содержащий простые страницы и блог.

От верстальщика мы получили макет страницы, содержащий:

Вот как это выглядит:

{% block head %}
	{% block title %}{% endblock %}
	{% block menu %}{% endblock %}
{% endblock %}

{% block page %}
	{% block content %}
	{% endblock %}
{% endblock %}

{% block footer %}
	{% block copyright %}
	{% endblock %}
{% endblock %}

Для всех указанных элементов мы создаем соответствующие блочные теги.

Простая страница ложится в этот макет - у нее есть только заголовок и тело.

Теперь перейдем к блогу. В блоге хотелось бы добавить правую колонку для вывода списка тегов и последних статей. Возможно мы захотим добавить правую колонку к каким-нибудь другим страницам сайта. Чтобы избежать копирования "двухколоночности", вынесем ее в отдельный шаблон, переопределив тело страницы у базового.

{% extend "base.htm" %}

{% block page %}
	{% block content %}
	{% endblock %}

	{% block sidebar %}
	{% endblock %}
{% endblock %}

В блоге будет несколько типов страниц:

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

{% extend "base_2col.htm" %}

{% block title %}
	Блог
{% endblock %}

{% block sidebar %}
	{% block tags %}
	{% endblock %}

	{% block recent %}
	{% endblock %}
{% endblock %}

Теперь приведем примеры внутренних страниц блога (все они наследуются от базовой страницы блога).

Список статей:

{% extend "blog/base.htm" %}

{% block content %}
	{% for article in article_list %}
		<a href="{% url article_view article.id %}">
		{{ article.title }}
		</a>
	{% endfor %}
{% endblock %}

Статья:

{% extend "blog/base.htm" %}

{% block title %}
	{{ article.title }} - {{ block.super }}
{% endblock %}

{% block content %}
	{{ article.text }}
{% endblock %}

Список статей, у которых есть определенный тег:

{% extend "blog/index.htm" %}

{% block title %}
	{{ tag.title }} - {{ block.super }}
{% endblock %}

{% block content %}
	{{ tag.title }}
	{{ tag.text }}

	{{ block.super }}
{% endblock %}

В данном случае, мы воспользовались еще одной хитростью. Ведь этот список ничем не отличается от простого списка статей - он просто отфильтрован по дополнительным параметрам. Поэтому мы унаследовали его от списка статей и при перекрытии тела использовали тег {{ block.super }} - вывести все содержимое родительского блока.

Как видно, каждый шаблон очень конкретен и отвечает только за свою функциональность. Ему нет необходимости знать о всей странице в целом.

Поклонники других шаблонных систем скажут, что для приведенного примера наследование не нужно. Действительно, то же самое можно реализовать, используя теги подстановки (include, ssi). Вот только логика и структура этих включений будет намного запутаннее. Получится, что статья должна знать, какие блоки будут на ее странице, и предоставлять данные для всех этих блоков. Тут на помощь приходит еще одна особенность Django - пользовательские теги.

Пользовательские теги

В нашем примере на странице статьи блога есть 7 блоков. Два из них - логотип и copyright - не нуждаются в данных. Для остальных пяти контроллеру необходимо предоставить шаблону данные. А именно:

Блоков могло быть намного больше, но непосредственное отношение к статье имеют только заголовок и тело статьи. Зачем статье знать, какие данные нужны этим блокам, откуда и как их получить? Абсолютно незачем - это не ее задача. Django предлагает нам следующее решение этой проблемы.

Для каждого из блоков мы можем написать свой тег, состоящий из мини-контроллера и шаблона. Контроллер знает, как получить данные, шаблон - как отобразить. В том месте, где нам необходим блок, мы вставляем его тег - и все! Например, можно вставить список тегов и последних статей на главную страницу. Главной странице нет необходимости что-либо знать о структуре нашего блога - только факт наличия и имена тегов, реализуемых блогом.

Вот пример тега для вывода списка последних статей в блоге:

# тег
@register.inclusion_tag('blog/article_recent_list.htm')
def blog_recent_articles_list():
	return {
		'article_list': Article.objects.filter(public=True)[:10],
	}

# шаблон
<h4>Последние статьи</h4>
<ul class="links">
	{% for article in article_list %}
	<li><a href="{% url article %}">{{ article.title}}</a></li>
	{% endfor %}
</ul>

Еще одним приемуществом такого подхода является то, что данные запрашиваются непосредственно при вставке тега. Если кэшировать несколько тегов, то будут кэшированы результаты их работы - повторно данные запрашиваться не будут! И не надо изобретать велосипеды, как тут ;)

Понятно, что при аккуратном подходе то же самое можно реализовать без наследования и без пользовательских тегов - подключениями и вызовом функций. Главное приемущество Django - это стройная, логичная и стандартная сруктура решения таких проблем.

Ссылки на официальную документацию:

Перепечатка и обсуждение на хабре.



Комментарии

Дмитрий 11.11.2008 06:31

Хм..похоже тебя слегка заспамили...

dpp 13.11.2008 04:23

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

Asrodnwh 02.05.2009 02:41

It's serious

margaret 23.06.2009 01:38

L5PhtP ega7Kl0dnDduqp6s2bnp1o

sergeyk 10.07.2009 08:49

Отличная статья, спасибо!
Как раз бился тут с множеством блоков с разной инфой на странице, сделал через include, но получалось сложно и криво. Теперь можно переписать красиво.

rtm 11.08.2009 10:26

extends на не extend :)

dpp 25.09.2009 01:03

точно! :)

Васька 24.09.2009 04:54

А не подскажете, как комментарии в шаблонах на Django делать?

Степан 16.12.2009 08:25

Спасибо за инфу. Полезно.

Валерий 23.12.2009 02:26

Спасибо за ценную информацию.

Slimfast 07.03.2010 12:53

Примите благодарность за интересный ресурс. Приятно было посетить.

Online chemist 12.05.2010 01:04

Статья содержательная. Полезно было ознакомиться.

Alex 13.05.2010 07:50

Интересно :)

Лидия 22.05.2010 05:26

Разные сонники. Толкование снов по ключевым словам, по одинадцати видам сонников.

Егор 11.07.2010 02:30

Все о творчестве великого американского музыканта, певца, короля рок-н-ролла Элвис Пресли.

Алексей 11.07.2010 10:18

Мир Музыки! Тексты песен отечественных и зарубежных исполнитей. Коллекция фотографий музыкантов. Собрание текстов песен, аккордов, табулатур и нот guitar pro.

Наталия 22.07.2010 12:44

Хороший выбор текстов песен с аккордами и табулатурами. Хороший выбор текстов песен с аккордами и табулатурами. Очень удобный поиск песен и табулатур Guitar pro по русскому и английскому алфавитам. Также вы можете найти тексты песен по году написания. Более 50 тысяч песен и табулатур.

Галина 15.08.2010 09:51

Фото знаменитостей. Самые известные женщины мира: актрисы кино и театра, модели, певицы, спортсменки, светские львицы, и просто известные красивые женщины!

Анатолий 25.09.2010 04:22

Фотографии известных музыкантов. Имеется широкий выбор фотографий популярных групп и исполнителей.

Юлия 20.10.2010 02:24

Большое собрание аккордов различных исполнителей. Удобная навигация.

Алексей 22.10.2010 11:29

На нашем сайте предоставлен большой выбор текстов песен с аккордами и табулатурами знаменитых отечественных, зарубежных музыкантов.

Вера 23.10.2010 09:27

Самое точное и правильное толкование сновидений по хорошему соннику по ключевым словам в алфавитном порядке.

Петр 27.11.2010 04:58

Много музыки. Аккорды и Табулатуры по названию группы и по названию песни, Guitar pro табы, тексты песен по названию исполнителя.

Анастасия 30.11.2010 02:29

Смешные анекдоты, приколы, афоризмы, рассказы и истории.

Валентина 03.12.2010 09:39

Большое собрание файлов караоке в формате mid и в формате kar. Тексты песен по названию группы и музыканта

Написать комментарий

Вы представились как:   e-mail: email (изменить)

Ссылки запрещены.


Copyright © DPP, 2008-2009