Шаблоны (templates). Начало

Курс по Django: https://stepik.org/a/183363

Архив проекта: lesson-6-coolsite.zip

На этом занятии мы с вами познакомимся с третьей компонентой паттерна проектирования MTV – шаблонами (templates). Что это такое? Вот смотрите, если мы откроем наш проект и запустим тестовый веб-сервер, то на главной странице увидим отображение одной короткой строчки. Как вы понимаете, полноценная HTML-страница содержит гораздо больше информации, в том числе, заголовок и подключаемые статические файлы. Конечно, если решать эту задачу «в лоб», то можно было бы написать в функции представления что-то вроде:

def index(request):
    return HttpResponse('''<!DOCTYPE html>
<html>
<head>
         <title></title>
</head>
<body>
 
</body>
</html>''')

Но, представьте, во что тогда превратится программа! Ее будет сложно читать, исправлять и, кроме того, изменение HTML-страницы повлечет изменение и самого приложения. Это полное безумие! Поэтому неудивительно, что все это выносится за пределы приложения и организуется в виде шаблонов HTML-страниц. И сейчас мы с вами узнаем, как в Django организованы шаблоны, где их хранить и как подключать.

Первое, что нужно знать, это как представляются шаблоны в Django. Работа с ними очень похожа на работу шаблонизатора Jinja, о котором я создавал серию занятий:

https://www.youtube.com/watch?v=cFJqMXxVNsI&list=PLA0M1Bcd0w8wfmtElObQrBbZjY6XeA06U

Далее, я буду полагать, что вы владеете этим материалом. Если это не так, то рекомендую с ним ознакомиться, прежде, чем двигаться дальше. Также рекомендую посмотреть русскоязычную документацию по шаблонам Django:

https://djbook.ru/rel3.0/topics/templates.html

Итак, предположим, что в качестве главной страницы мы бы хотели отобразить некоторый шаблон с именем index.html. Для этого, вначале нам нужно импортировать встроенный в Django шаблонизатор. По умолчанию в файле women/views.py это уже сделано вот такой строчкой:

from django.shortcuts import render

Здесь функция render как раз и отвечает за обработку шаблонов и выдачу их в формате HTTP-ответа клиенту. Пропишем эту функцию в представлении index. В самом простом варианте это будет выглядеть так:

def index(request):
    return render(request, '<путь к шаблону>')

Здесь первый параметр обязательно должен быть ссылкой request, а второй – путь к файлу шаблона. И здесь мы подошли ко второму важному вопросу: где должны располагаться шаблоны текущего приложения Women? По умолчанию, Django ищет шаблоны в подкаталоге templates нашего приложения. Создадим его. По идее, мы можем располагать здесь наши файлы шаблонов и все должно работать. Но есть один важный нюанс. При сборке всего проекта все шаблоны от всех приложений помещаются в единую папку templates проекта и если окажется в разных приложениях два одинаковых файла (с одинаковыми именами), то возникнет неопределенность (будет взят первый попавшийся файл). Чтобы этого не происходило, в templates приложения принято создавать еще один подкаталог с именем приложения. В нашем случае – women. И уже в него помещать файлы шаблонов. Тогда при сборке этот подкаталог целиком перенесется в каталог templates проекта и коллизий имен файлов не произойдет.

Итак, создадим в подкаталоге templates/women файл index.html с содержимым:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Главная страница</title>
</head>
<body>
 
</body>
</html>

Это будет наш первый простейший шаблон, представляющий главную страницу сайта. И, далее, в функции render укажем путь к этому шаблону:

def index(request):
    return render(request, 'women/index.html')

Все, при обновлении главной страницы в браузере, мы увидим этот шаблон. Как видите все достаточно просто и удобно.

Обратите внимание, для корректного отображения кириллицы все шаблоны рекомендуется сохранять в кодировке utf-8. Тем более, что сам Python, начиная с версии 3, по умолчанию использует юникод.

Давайте для примера добавим еще одну страницу и один шаблон на наш сайт – страницу «О сайте». Пропишем следующие пути (в women/urls.py):

urlpatterns = [
    path('', index, name='home'),
    path('about/', about, name='about'),
]

И функцию about в файле women/views.py:

def about(request):
    return render(request, 'women/about.html')

Добавим шаблон about.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>О сайте</title>
</head>
<body>
<h1>О сайте</h1>
</body>
</html>

И при переходе по адресу:

http://127.0.0.1:8000/about/

этот шаблон будет отображен.

Передача шаблонам параметров

Я много раз произносил слово «шаблон», но что оно означает? Если посмотреть на файлы index.html или about.html, то это просто текст, который загружается и отдается браузеру по соответствующему запросу. Все так, но в этих же файлах можно прописать конструкции для отображения информации, например, из БД. Давайте для начала сделаем так, чтобы на каждой странице был свой заголовок, переданный ей через параметр title. Это можно сделать так. В файлах index.html и about.html укажем переменную title:

<!DOCTYPE html>
<html>
<head>
         <title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
</body>
</html>

А в функциях представлений передать параметр title соответствующему шаблону:

def index(request):
    return render(request, 'women/index.html', {'title': 'Главная страница'})
 
def about(request):
    return render(request, 'women/about.html', {'title': 'О сайте'})

Все, теперь вместо title будет подставлена строка «Главная страница» или «О сайте» и отображаться на соответствующей странице. Удобно, правда? Вот в этом и есть роль шаблонов: они описывают структуру страницы, а ее наполнение происходит динамически в самой программе.

Можно выполнять передачу и более сложных данных, например, списков. Предположим, главное меню сайта определено через список:

menu = ["О сайте", "Добавить статью", "Обратная связь", "Войти"]

А, затем, в функциях представления, мы можем передать его как параметр:

def index(request):
    return render(request, 'women/index.html', {'menu': menu, 'title': 'Главная страница'})
 
def about(request):
    return render(request, 'women/index.html', {'menu': menu, 'title': 'О сайте'})

Для отображения этого списка в шаблонах, очевидно нужно перебрать в цикле его элементы и сформировать отдельные пункты:

<ul>
{% for m in menu %}
<li>{{m}}</li>
{% endfor %}
</ul>

Все, если теперь перейти на сайт и отобразить, например, главную страницу, то увидим этот список в виде HTML-тегов.

Создание базового шаблона (наследование шаблонов)

Наверное, многие из вас заметили, что у нас получилось два одинаковых шаблона и для них нарушается принцип программирования:

DRY – don’t repeat yourself (не повторяйся).

Чтобы устранить этот недостаток, обычно, создается базовый шаблон представления страниц на сайте, а уже потом он расширяется шаблонами отдельных страниц. Давайте сформируем шаблон базовой страницы в виде файла base.html со следующим содержимым:

<!DOCTYPE html>
<html>
<head>
         <title>{{title}}</title>
</head>
<body>
{% block mainmenu %}
<ul>
         {% for m in menu %}
<li>{{m}}</li>
         {% endfor %}
</ul>
{% endblock mainmenu %}
 
{% block content %}
{% endblock %}
</body>
</html>

А в дочерних расширим этот базовый шаблон:

- для index.html:

{% extends 'women/base.html' %}
 
{% block content %}
<h1>{{title}}</h1>
Содержимое главной страницы
{% endblock %}

- для about.html:

{% extends 'women/base.html' %}
 
{% block content %}
<h1>{{title}}</h1>
Содержимое страницы о сайте
{% endblock %}

Все, теперь никакого дублирования в наших шаблонах нет и мы можем достаточно просто описывать отдельные страницы сайта, просто меняя базовый шаблон base.html.

Отображение списка статей

Теперь, когда мы в целом разобрались, что из себя представляют шаблоны, давайте выполним чтение данных из таблицы women и отобразим список статей на главной странице сайта. Для этого в модуле women/views.py выполним импорт моделей:

from .models import *

А, затем, в функции представления index прочитаем записи из таблицы Women и передадим коллекцию шаблону:

def index(request):
    posts = Women.objects.all()
    return render(request, 'women/index.html', {'posts': posts, 'menu': menu, 'title': 'Главная страница'})

Видите, как просто это делается! Нам даже не нужно беспокоиться о подключении к БД. Фреймворк Django все эти операции берет на себя. Все что нам нужно – это выбрать нужные записи из таблицы и передать их в шаблон. В самом шаблоне index.html переберем эти записи и отобразим в виде списка на странице:

{% extends 'women/base.html' %}
 
{% block content %}
<h1>{{title}}</h1>
<ul>
         {% for p in posts %}
         <li>
                   <h2>{{p.title}}</h2>
                   {{p.content}}</p>
                   <hr>
         </li>
         {% endfor %}
</ul>
{% endblock %}

Все, возвращаясь на главную страницу нашего сайта, увидим список записей из БД. На мой взгляд, проще не придумаешь. Django этим и хорош. У него богатый функционал, но достаточно простой в использовании. Поэтому, даже начинающий веб-программист сможет с его помощью создавать грамотные сайты. На этом мы завершим первое знакомство с шаблонами. На последующих занятиях углубимся в эту тему и затронем фильтры и подключение статических файлов.

Курс по Django: https://stepik.org/a/183363

Видео по теме