Шаблоны Django. Наследование.
Раздел:
Programming /
Python
@
08.04.2008 |
Ключевые слова: Django шаблоны наследование ajax python
Автор: dpp
Источник: habrahabr
Прочитал статью «Фрагментарное кэширование в 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 - это стройная, логичная и стандартная сруктура решения таких проблем.
Ссылки на официальную документацию:
PS: Это перепечатка с моего блога http://dpp.su/. Кстати, там есть еще несколько менее интересных статей про Django.
Это интересно:
Распечатать статью
Вернуться в раздел:
Programming /
Python
Реклама: