Основы HTML позади, самое время вернуться в уютный мир бэкенда. Проекты «Анфиса» и Yatube уже научились реагировать на запросы и возвращать текстовую строку:
# ice_cream/views.py
from django.http import HttpResponse
# Главная страница
def index(request):
return HttpResponse('Главная страница')
Если view-функция может вернуть клиенту текстовую строку, то почему бы ей не вернуть HTML, ведь это тоже текстовая строка!
# ice_cream/views.py
from django.http import HttpResponse
# Главная страница
def index(request):
html_content = '<html><head><title>Анфиса для друзей</title></head><body>'
html_content += '<h1>Главная страница</h1>'
html_content += '</body></html>'
return HttpResponse(html_content)
Этот подход отлично работает (с технической точки зрения), но писать код будет ужасно неудобно:
- HTML-код может занимать сотни строк;
- непонятно, где начинается и заканчивается тот или иной HTML-тег;
- сложно ловить ошибки;
- синтаксис не подсвечивается;
- в коде перемешаны логика (Python) и отображение (HTML).
HTML — отдельно, Python — отдельно
Решение проблемы есть: HTML-код выносят в отдельные файлы (их называют HTML-шаблоны) и при необходимости подключают их через загрузчик — loader
:
from django.http import HttpResponse
# Импортируем загрузчик.
from django.template import loader
def index(request):
# Загружаем шаблон;
# шаблоны обычно хранят в отдельной директории.
template = loader.get_template('ice_cream/index.html')
# Формируем шаблон
return HttpResponse(template.render({}, request))
В результате в ответ на запрос будет отправлен HTML из шаблона ice_cream/index.html.Есть и более изящный синтаксис: вернуть пользователю HTML-код из шаблонов можно посредством функции render()
из раздела django.shortcuts. **Эта функция делает то же самое, что и код, приведённый выше, но часть операций прячет «под капот»:
from django.shortcuts import render
def index(request):
template = 'ice_cream/index.html'
return render(request, template)
Подключение HTML-шаблонов
В проекте «Анфиса для друзей» есть три view-функции и три HTML-шаблона:
- шаблон главной страницы: index.html;
- шаблон для для списка сортов мороженого: ice_cream_list.html;
- шаблон подробной информации про мороженое: ice_cream_detail.html.
Нехорошо, если они будут валяться в корневой директории проекта: надо их положить в специальную директорию. Но куда?Есть две традиционные схемы хранения шаблонов: на уровне приложения и на уровне проекта.
Хранение шаблонов на уровне приложения
В директориях приложений создают папки /templates/имя_приложения/: шаблоны, используемые в приложении, хранят в них. Такой способ называют «хранить шаблоны на уровне приложения». Если следовать этой схеме в проекте «Анфиса для друзей», то шаблоны приложения ice_cream должны лежать в директории /ice_cream/templates/ice_cream/:
anfisa
├── anfisa # Главная папка проекта
├── ice_cream # Папка приложения
│ ├── templates <-- Директория для шаблонов
│ │ └── ice_cream <-- Директория для шаблонов приложения ice_cream
│ │ ├── ice_cream_detail.html # Шаблон для отдельного мороженого
│ │ ├── ice_cream_list.html # Шаблон со списком мороженого
│ │ └── index.html # Шаблон главной страницы
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── test.py.py
│ ├── urls.py
│ └── views.py
└── manage.py
Этот вариант хорош в тех случаях, когда приложение планируется преобразовать в пакет Python (например, для того, чтобы можно было легко встроить его в другой проект). В работе с проектом Yatube таких планов нет, так что применим другой вариант.
Хранение шаблонов на уровне проекта
В корневой директории проекта создаётся папка /templates, в ней — папки, названные по именам приложений, и в этих папках хранятся шаблоны, относящиеся к приложениям проекта. Такой порядок хранения шаблонов называют «на уровне проекта».Согласно этому подходу шаблоны приложения ice_cream в проекте «Анфиса для друзей» должны лежать в директории /templates/ice_cream/:
anfisa
├── anfisa # Главная папка проекта
├── ice_cream # Папка приложения
├── templates <-- Директория для шаблонов
│ └── ice_cream <-- Директория для шаблонов приложения ice_cream
│ ├── ice_cream_detail.html # Шаблон страницы для отдельного мороженого
│ ├── ice_cream_list.html # Шаблон страницы со списком мороженого
│ └── index.html # Шаблон главной страницы
└── manage.py
Именно эту схему применим в проектах «Анфиса» и Yatube.
Настройки шаблонизатора Django
За настройки шаблонов в Django отвечает переменная TEMPLATES
в файле settings.py
:
BACKEND
: под этим ключом указывается, какой язык шаблонов используется в проекте.В Django существует два языка разметки шаблонов: Django Template Language (DTL) и Jinja2. Значение по умолчанию — DjangoTemplates. Оставим его.DIRS
: здесь указывается список директорий, где Django будет искать шаблоны. Чтобы Django искал шаблоны на уровне проекта, нужно добавить путь до папки с шаблонами.Путь к папке с шаблонами указывается от корневой директории проекта (от папки, где лежит manage.py).Адрес корневой директория хранится в константеBASE_DIR
, эта константа создаётся автоматически при создании проекта.Указывать путь к директории с шаблонами лучше не напрямую, а посредством методаpath.join
из стандартной библиотекиos
:os.path.join(BASE_DIR, 'templates')
.При таком подходе пути будут работать нормально на любой операционной системе: в Windows пути к директориям разделяются символом\
, а в macOS и Linux применяют/
; если указывать пути явно, без примененияos.path.join()
, при переносе проекта из системы в систему могут возникнуть проблемы.APP_DIRS
показывает, нужно ли искать шаблоны в папках приложений (на уровне приложений). Можно поменять наFalse
, но лучше не надо. Ведь админ-зона Django — это встроенное приложение, а в нём шаблоны хранятся на уровне приложения.
Вот как должен выглядеть список TEMPLATES
в том случае, когда шаблоны хранятся на уровне проекта:
# anfisa/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# Добавлено: Искать шаблоны на уровне проекта
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# Оставляем True: шаблоны встроенных приложений (например, админки)
# нужно искать в директориях приложений
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
]
},
}
]
Адреса директорий с шаблонами часто выносят в отдельные константы. Так код получается более читабельным.
# anfisa/settings.py
# Путь к директории с шаблонами вынесен в переменную:
TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# Добавлено: Искать шаблоны на уровне проекта
'DIRS': [TEMPLATES_DIR],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
]
},
}
]
Предстартовая проверка: структура папок в templates
В директории /templates должны быть созданы вложенные папки, названные по именам приложений. Это позволит точно определить, какому приложению какой шаблон соответствует: ведь в каких-то ситуациях имена шаблонов для разных приложений могут совпадать.
anfisa
├── anfisa/ # Главная папка проекта
├── ice_cream/ # Папка приложения
├── templates <-- Директория для шаблонов
│ └── ice_cream
│ ├── ice_cream_detail.html # Шаблон подробной информации о мороженом
│ ├── ice_cream_list.html # Шаблон списка мороженого
│ └── index.html # Шаблон главной страницы
└── manage.py