Курс по Django: https://stepik.org/a/183363
Архив проекта: 14_sitewomen.zip
На предыдущих
занятиях мы с вами подробно описали шаблон index.html для главной
страницы сайта. Давайте отредактируем еще один about.html с выводом
информации о сайте. В самом простом варианте его можно представить следующим
образом:
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<ul>
<li><a href="{% url 'home' %}">Главная</a></li>
{% for m in menu %}
{% if not forloop.last %}<li>{% else %}<li class="last">{% endif %}
<a href="{% url m.url_name %}">{{m.title}}</a>
</li>
{% endfor %}
</ul>
<h1>{{title}}</h1>
</body>
</html>
То есть, я
просто скопировал начало шаблона из index.html и вставил в
файл about.html.
Отредактируем
функцию about в файле views.py, добавив
передачу списка пунктов menu:
def about(request):
return render(request, 'women/about.html', {'title': 'О сайте', 'menu': menu})
После запуска
тестового веб-сервера и перехода по URL-адресу:
http://127.0.0.1:8000/about/
увидим страницу
«О сайте» с выводом главного меню.
Однако, как вы
уже догадались, дублирование информации в разных шаблонах нарушает известный
принцип:
DRY –
don’t repeat yourself (не повторяйся).
Для устранения
этого недостатка, обычно создается базовый (общий) шаблон, содержащий общие
элементы типовых страниц сайта, а уже потом он расширяется (дополняется
деталями) шаблонами отдельных страниц. Такое расширение называется
наследованием шаблонов. И сейчас мы на конкретном примере посмотрим, как это
можно сделать.
Если перейти в
документацию по фильтрам и тегам шаблонов фреймворка Django, то среди тегов
можно встретить следующие:
{%
block [наименование] %} …
{% endblock %}
{%
extends <базовый
шаблон>
%}
Я подробно о них
рассказывал в курсе по шаблонизатору Jinja2:
https://www.youtube.com/watch?v=cFJqMXxVNsI&list=PLA0M1Bcd0w8wfmtElObQrBbZjY6XeA06U
Но здесь повторюсь,
так как абсолютно ничего сложного в наследовании шаблонов нет.
Итак, вначале мы
с вами определим базовый шаблон. Его, как правило, создают на уровне всего проекта.
Поэтому создадим каталог templates в папке sitewomen, а в нем файл с
именем base.html. Однако если мы
сейчас попробуем обратиться к этому шаблону, например, в функции представления about:
def about(request):
return render(request, 'base.html', {'title': 'О сайте', 'menu': menu})
то увидим
ошибку, что шаблон не был найден. Это связано с тем, что маршрут templates/base.html не стандартный
и его нужно явно прописать для шаблонизатора. Для этого нужно перейти в файл settings.py пакета
конфигурации, найти параметр TEMPLATES и в коллекции DIRS прописать путь к
каталогу templates следующим образом:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / 'templates',
],
'APP_DIRS': True,
...
},
]
Все, теперь при
обновлении страницы «О сайте» мы увидим отображение базового шаблона.
Вернем прежний
шаблон в функции about:
def about(request):
return render(request, 'women/about.html', {'title': 'О сайте', 'menu': menu})
Давайте теперь
общие элементы страниц сайта вынесем в базовый шаблон base.html. Для этого я
скопирую все из файла about.html и посмотрим,
что здесь у нас общее. Очевидно, все начало, включая отображение главного меню.
А вот заголовок h1 и остальную часть страницы внутри тега body будем считать
изменяемой. Поэтому вместо тега h1 мы пропишем специальный шаблонный тег block следующим
образом:
{% block content %} {% endblock %}
Здесь content – это название
блока, вместо которого будет подставляться содержимое из наследуемых шаблонов.
Все, на этом
базовый шаблон у нас с вами завершен. Осталось лишь расширить его для шаблонов index.html и about.html. Сначала
перейдем в файл index.html и самой первой
строчкой пропишем еще один шаблонный тег extends:
{% extends 'base.html' %}
А ниже все
уберем, кроме отображения заголовка 1-го уровня и списка статей. Эта информация
должна быть размещена в блоке content базового
шаблона. Поэтому здесь, в дочернем нам следует переопределить этот блок content следующим
образом:
{% block content %}
<h1>{{title}}</h1>
<ul>
{% for p in posts %}
{% if p.is_published %}
<li>
<h2>{{p.title}}</h2>
<p >{{p.content}}</p>
<p ><a href="{% url 'post' p.id %}">Читать пост</a></p>
{% if not forloop.last %}
<hr>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endblock %}
То есть, мы
просто заключили в этот тег то, что должно быть помещено в блок content базового
шаблона.
Давайте
посмотрим, как это будет работать. Перейдем на главную страницу сайта и видим
полное содержимое HTML-документа, который мы с вами определили в базовом
шаблоне base.html и в дочернем index.html.
По аналогии
опишем расширение в шаблоне about.html:
{% extends 'base.html' %}
{% block content %}
<h1>{{title}}</h1>
{% endblock %}
Переходим по
ссылке на страницу «О сайте» и видим полноценную HTML-страницу с
заголовком 1-го уровня. В результате мы с вами ушли от дублирования в отдельных
шаблонах и можем достаточно просто определять страницы сайта, просто расширяя
базовый шаблон base.html.
Тег include
Помимо
расширения шаблонов можно еще делать включение одного шаблона в другой. О чем
здесь речь и для чего это нужно? Давайте представим, что мы бы хотели на
главной странице нашего сайта (в шаблоне index.html) дополнительно
отображать рубрики по известным женщинам в виде следующей навигационной панели
из ссылок:
<nav>
<a href="#">Актрисы</a> |
<a href="#">Певицы</a> |
<a href="#">Спортсменки</a>
</nav>
Причем делать
это и сверху перед списком, и внизу после списка. В результате у нас получается
дублирование фрагмента. Как раз чтобы этого избежать, применяется тег include, который
позволяет добавлять в шаблон данные из другого шаблона. Давайте это сделаем.
Вначале мы
создадим подкаталог includes в папке templates/women нашего проекта
для лучшей организации структуры файлов шаблонов. Внутри каталога includes разместим файл nav.html и скопируем в
него тег nav. А в шаблоне index.html подключим этот
файл с помощью тега include следующим образом:
{% extends 'base.html' %}
{% block content %}
{% include 'women/includes/nav.html' %}
<h1>{{title}}</h1>
<ul>
{% for p in posts %}
{% if p.is_published %}
<li>
<h2>{{p.title}}</h2>
<p >{{p.content}}</p>
<p ><a href="{% url 'post' p.id %}">Читать пост</a></p>
{% if not forloop.last %}
<hr>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% include 'women/includes/nav.html' %}
{% endblock %}
Причем сделали
это в двух местах. При обновлении главной страницы увидим тот же результат, что
и раньше, но при этом устранили дублирование фрагментов в шаблоне index.html.
Следует
отметить, что при включении, шаблон nav.html имеет доступ ко
всем параметрам, которые передаются в шаблон index.html. Например, в
нем можно вывести заголовок:
Если же нам
нужно запретить передачу переменных, то после пути к шаблону в теге include следует дополнительно
прописать ключевое слово only:
{% include 'women/includes/nav.html' only %}
А если нужно при
этом передать отдельные параметры, то это можно сделать с помощью ключевого
слова with следующим
образом:
{% include 'women/includes/nav.html' only with title='заголовок' %}
В итоге получаем
довольно гибкий инструмент включения фрагментов в наши шаблоны.
Курс по Django: https://stepik.org/a/183363