Предыдущая часть.
Наши предыдущие примеры шаблонов были небольшими фрагментами 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 %}будет строка, но вы так же можете использовать переменную, если не знаете имя родильского шаблона до момента запуска. Это позволяет вам делать классные и динамические сайты.
Конец второй главы.




