# Использование шаблонов, циклов и условий при создании сайта на Flask

В этой статье мы рассмотрим работу нескольких важных инструментов Flask: шаблоны, циклы и условия.

Нужно пройти 3 основных этапа:

  • создать несколько страниц на сайте;
  • добавить внутренние ссылки для перехода между страницами;
  • научиться отображать определённый контент на сайте.

Для начала работы нам понадобится подготовленный к работе Flask-приложения хостинг-контейнер на Джино и IDE на локальной машине, в которой будет вестись основная разработка проекта с последующим деплоем проекта непосредственно на хостинг.

# Начало работы

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

/opt/alt/python37/bin/virtualenv --python /opt/alt/python310/bin/python3 fvenv

Здесь /opt/alt/python310/bin/python3 — путь к интерпретатору Python 3.10. Все доступные версии Python и расположение их интерпретаторов доступны в разделе УправлениеТехническая информация.

После создания виртуального окружения активируем его:

source ./fvenv/bin/activate

Далее установим Flask:

pip install flask

# Output
…

Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.1 Werkzeug-2.1.2 click-8.1.3 flask-2.1.2 itsdangerous-2.1.2

Теперь создадим отдельную папку для файлов сайта. Её нужно разместить внутри папки с доменным именем, на котором будет работать сайт. На Джино эта папка находится внутри папки domains — путь к папке с файлами нашего сайта должен быть примерно таким: /domains/example.com/flask_app.

# Создание приложения

Переключимся на локальную машину в IDE, в которой будем разрабатывать приложение. В папке нашего сайта создадим первый файл app.py, который будет описывать работу сайта. Создадим простое отображение индексной страницы на главной странице сайта. Для этого в файл app.py поместим следующий код:

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def hello():
    return render_template('index.html')

Здесь мы:

  • вызвали из пакета flask класс Flask, который определяет, что код в этом файле будет Flask-приложением и вспомогательную функцию render_template для отображения шаблонов из HTML-файлов;
  • присвоили приложению имя app (app = Flask(__name__));
  • создали функцию hello, для которой использовали @app.route('/')— декоратор, который сделал её функцией отображения;
  • описали функцию hello и её обращение к HTML-шаблону с именем index.html.

Теперь создадим индексный файл, к которому обращаемся. В папке сайта создадим папку templates, в которой будем хранить шаблоны всех страниц нашего сайта. В этой папке создадим index.html со следующим содержимым:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask for web</title>
</head>
<body>
    <h1>Hello World!</h1>
    <h2>Welcome to your amazing FlaskApp!</h2>
</body>
</html>

На этом этапе можно загрузить проект на хостинг и проверить его работу, запустив приложение.

# Запуск Flask-приложения

Подробнее о запуске Flask-приложения на Джино.Хостинге можно прочитать в отдельной статье.

Структура файлов и папок нашего сайта на данном этапе будет выглядеть следующим образом:

-domains
--example.com
---passenger_wsgi.py
---flask_app
----app.py
----templates
-----index.html

Содержимое файла passenger_wsgi.py будет следующим:

import sys, os

INTERP = "{}/venv310/bin/python3".format(os.getenv("HOME"))

if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

sys.path.append(os.getcwd())

from flask_app.app import app as application

После сохранения изменений в passenger_wsgi.py нужно перезагрузить веб-сервер в разделе УправлениеНастройки веб-сервера. Откроем в браузере главную страницу нашего сайта:

«flask»

# Дополнительный модуль

Вернёмся в IDE и добавим на нашу индексную страницу дополнительный модуль, который будет выводить текущее время в формате UTC.

Для этого в начало файла app.py добавим строку import datetime, импортирующую в наше приложение модуль отображения времени. Для передачи времени в шаблон index.html создадим новую переменную utc_dt.

В итоге app.py будет иметь вид:

import datetime
from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def hello():
    return render_template('index.html', utc_dt=datetime.datetime.utcnow())

После этого нужно добавить отображение нашей новой переменной в шаблон index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask for web</title>
</head>
<body>
    <h1>Hello World!</h1>
    <h2>Welcome to your amazing FlaskApp!</h2>
    <h3>{{ utc_dt }}</h3>
</body>
</html>

Здесь мы добавили строку подзаголовка третьего уровня <h3>, в которой отображаем данные, передаваемые переменной. Сохраним все изменения и заново загрузим проект на хостинг. Перезагрузим веб-сервер и обновим страницу в браузере:

«flask»

# Инструмент наследования в шаблонах

Для иллюстрации работы этого инструмента создадим один базовый шаблон, из которого остальные шаблоны будут «подтягивать» общее оформление, тем самым делая все страницы сайта визуально одинаковыми.

В папке с шаблонами templates создадим файл base.html со следующим содержимым:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }
    </style>
</head>
<body>
    <nav>
        <a href="#">FlaskApp</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Здесь мы описали основное оформление наших страниц, оставив два отдельных блока для оформления заголовков и самого содержимого страницы: {% block title %} {% endblock %} и {% block content %} {% endblock %}. Это стандартные обозначения блоков для движка шаблонов Jinja, который используется во Flask (вы могли видеть это название при установке Flask). Эти блоки отвечают за вставку содержимого из других шаблонов.

Также здесь мы создали два заголовка для нашей главной страницы FlaskApp и About.

Теперь нам нужно изменить наш index.html, чтобы он работал с base.html. Изменим его содержимое:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Index {% endblock %}</h1>
    <h1>Hello World!</h1>
    <h2>Welcome to your amazing FlaskApp!</h2>
    <h3>{{ utc_dt }}</h3>
{% endblock %}

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

Обновим наше приложение на сервере и проверим его работу:

«flask»

Мы видим корректное отображение двух заголовков и содержимого индексной страницы. Теперь нужно создать страницу About и сделать активной ссылку для перехода на неё.

# Создание второй страницы

Страница с описанием нашего сайта создаётся так же, как индексная страница.

Нужно создать новую функцию в файле app.py:

# ...
@app.route('/about/')
def about():
    return render_template('about.html')

И после этого создать файл about.html в папке templates:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} About {% endblock %}</h1>
    <h3>FlaskApp is a web app that created with ...suddenly... Flask!.</h3>
{% endblock %}

Сейчас можно снова обновить приложение на сервере и посмотреть, как отображается новая страница. Для этого нужно в браузере указать адрес http://your_domain_name/about. Вот что мы увидим:

«flask»

# Создание внутренней ссылки

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

Нам нужно лишь использовать функцию url_for в файле base.html:

<a href="{{ url_for('hello') }}">FlaskApp</a>
<a href="{{ url_for('about') }}">About</a>

Здесь мы указываем, что нужно использовать функцию url_for для функций hello и about, описанных в файле app.py. Обратите внимание, что в данном случае обращение к функциям происходит исключительно по имени.

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

# Использование циклов и условий

Чтобы изучить работу циклов и условий, создадим ещё одну страницу нашего сайта. Это будет страница для комментариев.

Создадим её так же, как создавали предыдущие страницы.

Добавим в app.py функцию, в которой напрямую укажем текст комментариев:

@app.route('/comments/')
def comments():
    comments = ['Comment #1.',
                'I want comment too.',
                'Yeah! Lets start comment fight!',
                'Comment#43 =)'
                ]

    return render_template('comments.html', comments=comments)

Создадим шаблон comments.html, в который будем передавать комментарии из app.py. Также в этом шаблоне опишем визуальное оформление комментариев:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
        <div style="padding: 10px; background-color: #EEE; margin: 20px">
            <p style="font-size: 24px">{{ comment }}</p>
        </div>
        {% endfor %}
    </div>
{% endblock %}

И добавим ссылку на страницу с комментариями в базовый шаблон:

<a href="{{ url_for('hello') }}">FlaskApp</a>
        <a href="{{ url_for('comments') }}">Comments</a>
        <a href="{{ url_for('about') }}">About</a>

После сохранения всех сделанных изменений проверим работу нашего приложения:

«flask»

# Создание условий

Создадим условие, по которому чётные и нечётные комментарии будут выделяться различными цветами. Для этого изменим файл comments.html. Приведём его к следующему виду:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index % 2 == 0 %}
                {% set bg_color = '#ffff00' %}
            {% else %}
                {% set bg_color = '#00ffff' %}
            {% endif %}

            <div style="padding: 10px; background-color: {{ bg_color }}; margin: 20px">
                <p>#{{ loop.index }}</p>
                <p style="font-size: 24px">{{ comment }}</p>
            </div>
        {% endfor %}
    </div>
{% endblock %}

Здесь мы используем переменную loop.index — это переменная из Jinja, которой присваивается порядковый номер цикла. В этом случае if проверяет остаток деления на 2: если остаток равен нулю, то окну с комментарием присваивается один цвет, в противном случае — другой.

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

«flask»

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

…
{% for comment in comments %}
            <div style="padding: 10px;
                        background-color: {{ loop.cycle('#EEE', '#e6f9ff') }};
                        margin: 20px">
                <p>#{{ loop.index }}</p>
                <p style="font-size: 24px">{{ comment }}</p>

Можно также сохранить эти изменения, перезагрузить веб-сервер и проверить, что результат отображения не изменился, — всё то же чередование цветов.