Предыдущая часть.
Наши предыдущие примеры шаблонов были небольшими фрагментами HTML-кода, однако в реальной ситуации вы будете использовать Django для создания больших страниц. Отсюда возникает один из наиболее существенных вопросов веб-разработки – как уменьшить количество повторяющегося и избыточного кода в общих частях страниц, таких как навигация по сайту?
Классическое решение этой проблемы заключается в использовании инклюдов (includes), или “вложений” – особых директив в коде HTML, которые позволяют включать одну HTML-страницу в код другой. Действительно, Django поддерживает такой подход с помощью тега шаблона {% include %}
, который мы рассмотрели в статье Django Book: глава 2 – загрузка шаблонов. Но имеется и другой, более предпочтительный, способ решения, который называется Наследование шаблонов.
Кратко говоря, наследование позволяет вам построить базовый “скелет” шаблона, который содержит все части вашего сайта и определяет блоки, которые могут быть перезаписаны “дочерними” шаблонами.
Давайте рассмотрим пример реализации этого, создав более полный шаблон для нашего преставления current_datetime
, отредактировав файл timetemplate.html
:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>The current time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>It is now {{ current_date }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
Он выглядит достаточно хорошо, но что будет, если мы захотим создать шаблон для другого представления, например – hours_ahead
из статьи Django Book: третье представление – динамические URL-ы? Если мы опять хотим создать красивый и валидный HTML-шаблон – нам придётся делать что-то вроде такого:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>Future time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
Очевидно, что мы дублируем много HTML-кода. Представьте, что у нас есть типичный сайт, с панелью навигации, несколько CSS, возможно JavaScript – нам придётся добавлять весь избыточный код в каждый шаблон.
Наследование шаблонов позволяет избежать этого, определив общие части для этих шаблонов, сохранить их отдельными файлами, и затем подключить к каждому шаблону.
Например, вы могли бы сохранить заголовок страницы в шаблоне с именем header.html
:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>
А окончание страницы – в файле footer.html
:
<hr> <p>Thanks for visiting my site.</p> </body> </html>
С использованием инклюдов такое реализовать очень просто. Однако, тут есть несколько проблем. Например, обе страницы используют заголовок страницы – <h1>My helpful timestamp site</h1>
, но он не может быть записан в header.html
, так как <title>
на страницах разный. Если мы включим <h1>
в заголовок – нам придётся включить туда и <title>
, и это не позволит нам настраивать страницы.
Система наследования в шаблонах Django решает эту проблему. Вместо определения того, как части страницы будут общими – вы можете определить какие из них будут разными.
Первый шаг – определить базовый шаблон – скелет страницы, который потом будет заполнен дочерними шаблонами. Вот пример нашего текущего шаблона:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
Этот шаблон мы назовём base.html
, и он описывает простой HTML-скелет документа, который мы будем использовать для всех страниц сайта. Далее уже наши дочерние шаблоны будут перезаписывать, добавлять или удалять содержимое блоков. Сохраните его в вашей директории templates
с именем base.html
.
Тут мы использовали тег шаблона, который вы раньше не встречали – {% block %}
. Всё, что он делает – это указывает системе шаблонов, что дочерний шаблон может перезаписать этот участок шаблона.
Теперь, мы можем отредактировать наш шаблон timetemplate.html
, что бы использовать эту возможность:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
Теперь, давайте создадим шаблон для представления hours_ahead
(мы предлагаем вам самим изменить само представление, что бы оно использовало систему шаблонов, вместо кода непосредственно в коде представления). Вот как он может выглядеть:
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
Правда – замечательно? Каждый шаблон только свой уникальный код, больше нет никакой избыточности. Если вам необходимо сделать изменения, касающиеся всего сайта – достаточно отредактировать файл base.html
, и все шаблоны немедленно их отобразят.
Вот как это работает. Когда загружается шаблон timetemplate.html
, система шаблонов встречает тег {% extends %}
, который означает что это дочерний шаблон. Система сразу же подгружает родительский шаблон, в данном случае это base.html
.
Далее, система находит три тега {% block %}
в шаблоне base.html
, и заменяет их значение блоками из дочерней страницы. Таким образом, мы заменяем блоки {% block title %}
и {% block content %}
.
Заметьте, что так как дочерний шаблон не определяет блок footer
– система шаблонов использует значение из родительского шаблона. Содержимое блока {% block %}
в родительском шаблоне всегда используется как “запасной”, если не указано ничего в дочернем.
Наследование не затрагивает контекст шаблона. Другими словами, каждый шаблон в дереве наследования будет иметь доступ к переменным вашего шаблона их контекста.
Вы можете использовать любое количество уровней наследования. Обычно используют трёх-уровневый подход:
- Базовый шаблон
base.html
, который описывает общий вид вашего сайта. Он меняется редко – или даже вообще никогда. - Создаются
base_SECTION.html
шаблоны, один для каждой “секции” сайта (например –base_photos.html
иbase_forum.html
). Эти шаблоны дополняютbase.html
и содержат в себе специфичные для раздела стили и дизайн. - Создаются отдельные шаблоны для каждого типа страницы, таких как форум или фотогалерея, которые дополняют шаблон секции.
Такой подход позволяет максимально использовать один и тот же код, и упрощает добавление объектов в общие части сайта, такие как панель навигации.
Вот несколько указаний для работы с наследованием шаблонов:
- При использовании тега
{% extends %}
– он должен быть первым тегом в шаблоне. Иначе наследование не будет работать. - Как правило, чем больше тегов
{% block %}
в базовом шаблоне – тем лучше. Помните, что дочерние шаблоны не обязательно должны заполнять все родительские блоки, поэтому вы можете создать их заранее в необходимых местах и количестве, а затем заполнять по мере необходимости из дочерних шаблонов. - Если вы видите, что повторяете один и тот же код во многих шаблонах – возможно вам стоит переместить этот код в блок
{% block %}
родительского шаблона. - Если вам необходимо использовать код из блока родительского шаблона – используйте тег
{{ block.super }}
, который является особой переменной, позволяющей обратиться к родительскому шаблону. Это может быть полезно, если вы хотите дополнить родительских блок, а не полностью его перезаписывать. - Нельзя использовать тег
{% block %}
с одним и тем же именем несколько раз в одном шаблоне. Это ограничение вызвано тем, что блоки работают в “обоих направлениях”. Т.е., тег{% block %}
не только указывает место, в котором будет располагаться содержимое, но и выполняет его наполнение в родительском шаблоне. Поэтому, если у вас будет два блока с одним именем в шаблоне – родительский шаблон просто не будет знать, какой из них необходимо использовать. - Шаблон, который вы передаёте с помощью
{% extends %}
загружается используя тот же метод, который используетсяget_template()
. Таким образом, имя шаблона добавляется к параметруTEMPLATE_DIRS
. - В большинстве случаев, аргументом к
{% extends %}
будет строка, но вы так же можете использовать переменную, если не знаете имя родильского шаблона до момента запуска. Это позволяет вам делать классные и динамические сайты.
Конец второй главы.