Шаблонизация.

Ранее в примерах мы возвращали HTML в виде строковых литералов. Выглядело это как-то так:

return '''<html>
  <head>
    <title>Hello!</title>
  </head>
  <body>
    <h1>World</h1>
  </body>
<html>
'''

Многострочные литералы нам, конечно же, помогают в этом деле. Но большие объёмы HTML-разметки становится сложно редактировать, да и код засоряется. Можно вынести HTML в файл, читать его и отдавать содержимое. Но как быть, если мы хотим изменить что-то в тексте? Можно использовать регулярные выражения или замену подстрок, но, как говорится, есть способ лучше — шаблонизация!

Напомню, шаблонизацией называется превращение некоей заготовки (шаблона), содержащей неизменную часть некоего текста, в финальную версию этого текста путём подстановки значений и выполнения других преобразований, описанных в шаблоне с использованием специального языка.

Flask, хоть и позиционирует себя как микрофреймворк, поставляется вместе с шаблонизатором (большинство микрофреймворков так не делает) Jinja2. Jinja2 — это популярный и мощный шаблонизатор, используемый повсеместно, от web-фреймворков до генераторов конфигурационных файлов (да, Jinja2 может формировать не только HTML!). Но именно во Flask он уже настроен так, чтобы начать использовать его было максимально просто!

Вызов шаблонизатора.

Для того, чтобы вернуть из обработчика HTML, построенный на базе шаблона, вам нужен этот самый шаблон. Предположим, что у нас есть таковой — файл index.html:

<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    {% if header %}
    <h1>{{ header }}</h1>
    {% endif %}
    {{ text }}
  </body>
<html>

В коде же мы пишем что-то такое:

from flask import render_template

@app.route('/')
def index():
    return render_template(
        'index.html',
        title='Hello, World!',
        header='Welcome',
        text='TODO'
    )

Разберём сначала код. Функция render_template принимает имя файла шаблона — это обязательный позиционный аргумент. Все именованные аргументы становятся контекстом шаблонизации (ниже я расскажу про контекст).

Теперь посмотрим на шаблон. В основном, содержимое шаблона представляет собой обычный HTML. Для шаблонизатора важны только подстановки значений вида {{ title }} и управляющие конструкции вида {% if header %}. В этом конкретном шаблоне подставляются заголовок страницы title, заголовок перед текстом header и текст страницы text. При этом условие if header позволяет не вставлять тег h1, если заголовок header не задан. Значения для подстановки ищутся по соответствующим именам в том самом контексте. В примере кода выше заданы все три значения, поэтому сервер отдаст такой HTML:

<html>
  <head>
    <title>Hello, World!</title>
  </head>
  <body>
    <h1>Welcome</h1>
    TODO
  </body>
<html>

Поиск шаблонов.

Когда я выше упоминал содержимое шаблона и кода, его использующего, я не сказал, как следует располагать файлы с шаблонами относительно кода. По умолчанию принято шаблоны помещать в поддиректорию templates в директории с точкой входа вашего web-приложения. Например, если ваше приложение определено в файле hello_world.py, а шаблон называется index.html, то структура файлов и директорий будет такой:

$ tree
.
├── hello_world.py
└── templates/
    └── index.html

Безопасность шаблонов.

Когда значение подставляется в шаблон, всё, хоть отдалённо похожее на HTML-теги, экранируется. Это защищает пользователя сайта от злонамеренной вставки в контент страницы невидимых блоков со скриптами и другого "опасного" содержимого. Такая защита особенно важна, когда шаблонизатор отображает данные, введённые другими пользователями.

Если же вы уверены, что знаете, что делаете, то вы можете указать при подстановке в конкретном месте суффикс |safe и значение из контекста будет подставлено "как есть":

<div>{{ content|safe }}</div>

Суффиксы вроде |safe называются фильтрами и их существует довольно много. Можно даже делать свои фильтры и тем самым расширять возможности шаблонизатора.

Прочие возможности.

Я специально не стал здесь даже просто перечислять все возможности Jinja2, а только лишь показал, как начать использовать простые шаблоны. У Jinja2 есть прекрасная документация с множеством примеров. Кроме того в Интернете можно найти множество статей по использованию именно этого шаблонизатора — повторю, он очень популярен!

Ссылки.

  • Официальный сайт Jinja2.

Задание.

  1. В созданном ранее Flask-приложении добавьте обработчик пути /99-bottles, который должен будет отдать HTML-страницу, построенную с помощью шаблонизатора.
  2. Сделайте так, чтобы страница содержала маркированный список <ul> c элементами <li>. Элементы должны содержать строки "99 бутылок чего-то стояло на столе, одна упала и разбилась.", "98 бутылок..." и так далее вплоть до "Нет больше бутылок на столе.". Вам пригодится цикл for в его шаблонной версии.
Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →