Обычно шаблоны не пишут целиком в одном файле: их разделяют на части и сохраняют в отдельных файлах.При подготовке ответа возвращаемая HTML-страница монтируется из этих фрагментов, как из конструктора Lego, и в собранном виде отправляется клиенту.Для такой практики есть несколько веских причин.
- Как правило, страницы проекта содержат повторяющиеся фрагменты: шапка сайта, подвал, блок навигации. Карточки товаров в интернет-магазине или анонсы статей на информационном ресурсе тоже свёрстаны одинаково. Эти элементы в одинаковом виде присутствуют на многих страницах. Код каждого такого элемента сохраняют в отдельный файл, а при рендеринге встраивают их в общий шаблон. В этом тоже реализация принципа DRY: если, например, потребуется изменить шапку сайта, изменения вносятся в один файл. В ином случае пришлось бы редактировать десятки шаблонов, исправляя шапку в каждом из них.
- Шаблон для нового типа страницы можно подготовить за считанные минуты, собрав его из готовых фрагментов, как из конструктора.
В Django-шаблонах кроме стандартных HTML-тегов поддерживаются свои, специальные теги. В отличие от HTML-тегов теги шаблонов замыкаются в фигурные скобки.
Тег {% include %}: собираем шаблон из фрагментов
Повторяющиеся части шаблонов можно вынести в отдельные файлы и по необходимости подключать к любой странице. Для подключения применяют тег {% include 'адрес_подключаемого шаблона' %}
. Подключаемые шаблоны обычно хранят в специальной папке includes/
в директории с шаблонами приложения, если они применяются только в шаблонах приложения. Если же подключаемые шаблоны применяются в масштабе всего проекта — их сохраняют в папке includes/
в корневой директории шаблонов.
anfisa
├── ...
├── templates
| ├── ice_cream
│ | ├── includes <-- Папка для повторяющихся частей шаблонов приложения
│ | ├── ice_cream_detail.html
│ | ├── ice_cream_list.html
│ | └── index.html
│ └── includes <-- Папка для повторяющихся частей шаблонов проекта
│ ├── header.html
│ └── footer.html
└── ...
Для включения кода из одного файла в другой применяют тег шаблонизатора {% include %}
. Аргументом для этого тега указывается относительный адрес шаблона, код которого надо включить в файл: {% include 'адрес_файла.html' %}
. Если повторяющиеся части — код шапки сайта и подвала — вынести в отдельные файлы, код главной страницы может выглядеть примерно так:
<!-- templates/ice_cream/index.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
</head>
<body>
<header>
<!-- Это тег шаблонизатора include, сюда будет включён код
из файла includes/header.html -->
{% include 'includes/header.html' %}
</header>
<main>
Информация на главной странице
</main>
<footer>
{% include 'includes/footer.html' %}
</footer>
</body>
</html>
Другие страницы будут выглядеть аналогично:
<!-- templates/ice_cream/ice_cream_list.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
</head>
<body>
<header>
{% include 'includes/header.html' %}
</header>
<main>
Список мороженого быть тут должен
</main>
<footer>
{% include 'includes/footer.html' %}
</footer>
</body>
</html>
В остальных шаблонах указывается, что они являются дочерними для базового шаблона (или «расширяют» его: англ. extend — «расширять»):
<!-- templates/ice_cream/index.html -->
{% extends 'base.html' %}
<!-- templates/ice_cream/ice_cream_list.html -->
{% extends 'base.html' %}
Когда view-функция Django вызывает какой-то шаблон, например…
...
def index(request):
template = 'ice_cream/index.html'
return render(request, template)
…а в вызванном шаблоне видит тег {% extends 'base.html' %}
…
<!-- templates/ice_cream/index.html -->
{% extends 'base.html' %}
…будет вызван шаблон base.html. Если в базовом шаблоне есть теги {% include %}
— в них выполнятся все инструкции.Но самое важное: дочерние шаблоны могут менять информацию в блоках, описанных в родительском.
Комментарии
Django-шаблоны поддерживают комментарии — строки, которые игнорируются при интерпретации кода.Комментарии могут быть трёх типов:
- Однострочные комментарии: записываются между символами
{#
и#}
, - Многострочные комментарии пишут между конструкциями
{% comment %}
и{% endcomment %}
. - Стандартные HTML- комментарии: их тоже можно применять, если комментируемая строка не содержит переменных шаблона; записывается между конструкциями
<!--
и-->
.
{% comment "Опциональный текст, поясняющий смысл закомментированного" %}
1 вариант: многострочный.
<p>Этот кусок шаблона временно отключён {{ create_date|date:"c" }}</p>
<p>Уходя, гасите свет.</p>
{% endcomment %}
{# 2 вариант: однострочный; можно комментировать переменные {{ varaible }} #}
{# А можно применять для заметок #}
<!-- 3 вариант: только для HTML кода. Не вставляйте в него перемнные -->
Тег {% block %}: изменяем содержимое базового шаблона из дочернего
Пример шаблона base.html с блоком content
:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
</head>
<body>
<header>
{% include 'includes/header.html' %}
</header>
<main>
{% block content %}
Контент не подвезли :(
{% endblock %}
</main>
<footer>
{% include 'includes/footer.html' %}
</footer>
</body>
</html>
Теперь из любого дочернего шаблона можно изменить содержимое блока content
.На главной:
<!-- templates/ice_cream/index.html -->
{% extends 'base.html' %}
{% block content %}
Это главная страница проекта «Анфиса для друзей»!
{% endblock %}
Теперь, если из view-функции будет вызван шаблон ice_cream/index.html, события будут развиваться так:
- Вызывается шаблон ice_cream/index.html.
- В файле index.html шаблонизатор видит тег
{% extends 'base.html' %}
и вызывает шаблон base.html. - В шаблоне base.html Django обнаруживает теги
include
и встраивает код из подключаемых файлов header.html и footer.html. - В изначально вызванном файле index.html Django видит тег
{% block content %}
с неким содержимым, ищет одноимённый блок в базовом шаблоне и, если находит, встраивает содержимое блока из дочернего файла в базовый.
При вызове страницы со списком сортов мороженого произойдёт всё то же самое, но содержимое блока {% block content %}
может быть иным — соответственно, иным будет и содержимое веб-страницы, которая вернётся пользователю:
<!-- templates/ice_cream/ice_cream_list.html -->
{% extends 'base.html' %}
{% block content %}
Список мороженого быть тут должен
{% endblock %}
Дочерние шаблоны получаются простыми и легкоизменяемыми.Если в тег block
ничего не передано или он отсутствует в дочернем шаблоне — на веб-страницу будет выведено значение, которое предустановлено в базовом шаблоне. Сейчас в коде базового шаблона в блоке content
предустановлен текст Контент не подвезли :(
.Но предустановленного значения может и не быть, оно необязательно.
Структура
Поскольку блоки в директории /includes и базовый шаблон base.html относятся не к какому-то определённому приложению, а ко всему проекту в общем, то будет удобно хранить их не в папке с шаблонами приложения, а отдельно, прямо в директории /templates. И структура шаблонов проекта «Анфиса для друзей» в такой логике будет выглядеть так:
anfisa
├── anfisa/ # Главная папка проекта
├── ice_cream/ # Папка приложения
├── templates <-- Директория для шаблонов
│ ├── ice_cream <-- Директория для шаблонов приложения ice_cream
│ │ ├── ice_cream_detail.html # Шаблон страницы для отдельного мороженого
│ │ ├── ice_cream_list.html # Шаблон страницы со списком мороженого
│ │ └── index.html # Шаблон главной страницы
│ ├── includes <-- Директория с подключаемыми шаблонами
│ │ ├── header.html # Верхняя часть страниц
│ │ └── footer.html # Нижняя часть страниц
│ └── base.html # Базовый шаблон
└── manage.py