Как создать и запустить приложение на Flask и Python - Академия Selectel

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

Flask — один из самых популярных веб-фреймворков для Python. Его используют как новички, так и профессиональные разработчики. В этой инструкции мы покажем, как с ним работать, создав простое приложение.

Что такое Flask

Flask — это микрофреймворк для веб-разработки на Python. Он позволяет подключать только необходимые модули, что дает разработчику полную свободу в проектировании архитектуры приложения. Flask легко расширяется за счет множества доступных дополнений, поддерживает шаблонизатор Jinja2 и асинхронность.

Фреймворк используется в самых разных сценариях: от небольших веб-приложений и API до микросервисов и корпоративных инструментов. Его выбирают за простоту настройки, минимальные накладные расходы и хорошую совместимость с внешними библиотеками.

Установка и настройка Flask

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

Установка Python

Flask работает на Python, поэтому сначала убедимся, что он установлен.


    python3 --version

Если Python уже присутствует в системе, терминал выдаст его версию в формате Python 3.x.x. В ином случае установим его:


    sudo apt update && sudo apt install python3 -y

А также добавим необходимые утилиты: инструмент для управления виртуальными окружениями python3-venv и менеджер пакетов pip:


    sudo apt install python3-venv python3-pip -y

Создание виртуального окружения

Использование виртуального окружения позволяет изолировать зависимости проекта от глобальной системы, предотвращая конфликты между библиотеками. Для каждого отдельного проекта необходима своя виртуальная среда.

Перед началом работы создадим каталог для проекта и перейдем в него:


    mkdir ~/trex_cloud && cd ~/trex_cloud

Создадим виртуальное окружение:


    python3 -m venv .venv

Здесь .venv — имя окружения. Принято называть его venv/.venv или env/.env, но можно выбрать любое название.

Активируем окружение:


    source .venv/bin/activate

Примечание

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

Установка Flask

Когда окружение настроено, установим сам Flask:


    pip install flask

Запросим версию Flask для проверки корректности установки:


    python -m flask --version

Структура приложения на Flask

Структура приложения во Flask может варьироваться в зависимости от сложности проекта. В самом простом случае можно обойтись одним файлом app.py, но для удобства масштабирования и поддержки проектов принято разделять код на логические модули.

Рассмотрим базовую структуру приложения:


    trex_cloud/
├── app.py
├── config.py
├── forms.py
├── migrations/
├── models.py
├── static/
├── templates/
└── .venv/

Все компоненты приложения разделены по своим ролям:

  • app.py — основной файл приложения, запускающий сервер Flask;
  • config.py — файл конфигурации проекта;
  • forms.py — классы веб-форм;
  • migrations/ — каталог для миграций базы данных;
  • models.py — описание моделей данных для базы данных;
  • static/ — статические файлы (CSS, JavaScript, изображения);
  • templates/ — HTML-шаблоны страниц;
  • .venv/ — виртуальное окружение проекта.

Как запустить приложение на Python и Flask

Теперь можно приступить к созданию самого приложения. Flask позволяет быстро развернуть веб-сервер и начать работу с минимальным количеством кода.

Создание app.py

Начнем с создания основного файла приложения. В директории проекта создадим файл app.py, откроем его в любом текстовом редакторе и добавим следующий базовый код на языке Python:


    from flask import Flask

app = Flask(__name__)

if __name__ == '__main__':
    app.run(debug=True)

Здесь мы задали основной объект app, который является экземпляром Flask, а также указали app.run для запуска встроенного сервера разработки.

Создание первого маршрута

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


    from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return 'Trex likes Flask!'

if __name__ == '__main__':
    app.run(debug=True)

В этом коде:

  • декоратор @app.route(‘/’) связывает URL-адрес / с функцией home();
  • функция home() выполняется при обращении к / и возвращает строку «Trex likes Flask!», которая отображается в браузере.

Запуск сервера Flask

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


    python app.py

Если приложение запустилось без ошибок, вы увидите следующий вывод:


    * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 118-905-759

Проверка работы приложения

Чтобы проверить работу приложения, откроем браузер и перейдем по адресу http://127.0.0.1:5000/.

Если все настроено правильно, мы увидим сообщение «Trex likes Flask!»:

Открыто окно браузера, в нем надпись Trex likes Flask!

Работа с приложением во Flask

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

Создание и использование HTML-шаблонов

Во Flask поддерживается шаблонизация с использованием Jinja2, который позволяет динамически формировать HTML-страницы, создавать макеты и переиспользовать элементы интерфейса.

При создании страниц мы также будем использовать CSS-стили и Bootstrap — это популярный CSS-фреймворк, который помогает быстро создавать адаптивные и красивые веб-страницы. Он предоставляет готовые стили для кнопок, форм, навигации и других элементов интерфейса.

По умолчанию Flask ищет шаблоны в папке templates/, которая должна находиться в корне проекта. Если не создать этот каталог, Flask не сможет рендерить HTML-файлы.

Создадим необходимые для работы каталоги:


    mkdir templates static

Базовый HTML-шаблон

Базовый шаблон будет использован для всех остальных страниц сайта. Создадим templates/base.html со следующим содержимым:


    


    
    
    <title>{% block title %}Тирекс в облаке{% endblock %}</title>
    



    <header class="bg-danger text-white text-center py-3">
        <h1 class="h3">Тирекс расскажет про облака!</h1>
    </header>

    <main class="container my-4">
        {% block content %}{% endblock %}
    </main>

    <footer class="bg-dark text-white text-center py-3 mt-4">
        <p class="mb-0">&copy; 2025 Trex</p>
    </footer>

    


Здесь:

  • {% block title %}{% endblock %} — динамический заголовок страницы;
  • {% block content %}{% endblock %} — место для контента страницы.

Также мы добавили ссылки на Bootstrap 5, использовали его классы (container, bg-dark и др.) и добавили стилизованный header и footer. Теперь все страницы, которые будут наследовать base.html, автоматически получат стили Bootstrap.

Главная страница

Теперь создадим шаблон templates/index.html, который будет наследовать base.html:


    {% extends "base.html" %}

{% block title %}Главная страница{% endblock %}

{% block content %}
<div class="text-center">
    <h2 class="mt-4">Добро пожаловать в облако Selectel!</h2>
    <p class="lead">Привет, я Тирекс! Здесь ты узнаешь, как работают дата-центры, виртуальные машины и облачные сервисы!</p>
</div>
{% endblock %}

Здесь мы:

  • подключили базовый шаблон: {% extends «base.html» %};
  • определили заголовок страницы: {% block title %}…{% endblock %};
  • заполнили {% block content %} содержимым.

Подключение статических файлов

Чтобы страница выглядела лучше, создадим CSS-файл static/style.css, в котором опишем стили для используемых HTML-элементов: 


    body {
    font-family: Arial, sans-serif;
    margin: 20px;
    background-color: #f5f7fa;
}

header {
    background: #ff5757;
    color: white;
    padding: 15px;
    text-align: center;
}

h2 {
    color: #d63a3a;
}

ul {
    list-style: none;
    padding: 0;
}

li {
    background: white;
    border-left: 5px solid #d63a3a;
    padding: 10px;
    margin: 5px 0;
}

Теперь следует подключить этот CSS-файл в базовом шаблоне base.html. Поместите следующую строку после <link href=»https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css» rel=»stylesheet»>:


    

Рендеринг шаблона

Чтобы Flask отобразил HTML-шаблон, нужно использовать функцию render_template(). Внесем изменения в app.py:


    from flask import Flask, render_template

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run(debug=True)

Перезапустите приложение. После запуска сервера отройте страницу http://127.0.0.1:5000/. Браузер загрузит index.html, используя base.html как шаблон.

В браузере открылась созданная нами страница.

Передача данных в шаблон

Flask позволяет передавать данные в HTML-шаблоны с помощью переменных. Например, добавим список облачных сервисов Selectel и отобразим его в шаблоне.

Создадим templates/services.html:


    {% extends "base.html" %}

{% block title %}Облачные сервисы{% endblock %}

{% block content %}
<div class="container mt-4">
    <h2 class="text-center">Облачные решения от Selectel</h2>
    <ul class="list-group mt-3">
        {% for service in services %}
            <li class="list-group-item">{{ service }}</li>
        {% endfor %}
    </ul>
</div>
{% endblock %}

В этом шаблоне используется {% for service in services %} для перебора списка, а {{ service }} вставляет название сервиса в HTML.

Дополним app.py вслед за функцией home:

@app.route(‘/services’)
def services():
    cloud_services = [«Виртуальные серверы», «Облачное хранилище», «Kubernetes»]
    return render_template(‘services.html’, services=cloud_services)

Теперь, если открыть http://127.0.0.1:5000/services, в браузере отобразится страница со списком сервисов.

На странице отображаются облачные решения.

Обработка входящих запросов

Любое веб-приложение взаимодействует с пользователями через HTTP-запросы. В этом разделе мы разберемся, как Flask обрабатывает входящие запросы, научимся передавать параметры в URL, разберем методы GET и POST и подключим обработку ошибок.

Для этого обновим структуру приложения. Оно должно уметь:

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

Обработка GET-запросов

GET-запросы используются для получения информации. Например, главная страница уже загружается через такой запрос.

Немного изменим templates/index.html, добавив ссылку на будущую страницу со списком серверов:


    {% extends "base.html" %}

{% block title %}Главная -- Тирекс в облаках{% endblock %}

{% block content %}
<div class="container mt-4 text-center">
    <h2 class="mb-3">Добро пожаловать в облака Selectel!</h2>
    <p class="lead">Привет, я Тирекс! Здесь ты узнаешь, как работают дата-центры, виртуальные машины и облачные сервисы!</p>
    <a href="/servers" class="btn btn-danger mt-3">Посмотреть доступные серверы</a>
</div>
{% endblock %}

Теперь при открытии http://127.0.0.1:5000/ пользователь увидит главную страницу и новую кнопку «Посмотреть доступные серверы».

Отображается новая кнопка.

Теперь добавим страницу /servers, в которой будем передавать список серверов в шаблон, добавим фильтр по GET-параметру type и будем генерировать ссылки на соответствующие страницы.

Создадим templates/servers.html, который будет показывать список серверов:


    {% extends "base.html" %}

{% block title %}Серверы Selectel{% endblock %}

{% block content %}
    <h2 class="mb-4">Доступные серверы</h2>
    
    
        <div class="row">
            <div class="col-md-6">
                <label for="type" class="form-label">Фильтр по типу:</label>
                
                    Все
                    GPU
                    Хранилище
                    Вычисления
                
            </div>
            <div class="col-md-6 d-flex align-items-end">
                <button type="submit" class="btn btn-danger">Фильтровать</button>
            </div>
        </div>
    

    <div class="row">
        {% for server in servers %}
            <div class="col-md-4">
                <div class="card mb-3 border-danger">
                    <div class="card-body">
                        <h5 class="card-title">{{ server.name }}</h5>
                        <p class="card-text">Тип: {{ server.type }}</p>
                        <a href="/server/{{ server.id }}" class="btn btn-outline-danger">Подробнее</a>
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>
{% endblock %}

Дополним app.py в начале файла:


    from flask import Flask, render_template, request

А также добавим еще один маршрут и напишем к нему функцию:


    @app.route('/servers')
def list_servers():
    servers = [
        {"id": 1, "name": "GPU-сервер", "type": "GPU"},
        {"id": 2, "name": "Хранилище данных", "type": "Storage"},
        {"id": 3, "name": "Вычислительный сервер", "type": "Compute"}
    ]

    server_type = request.args.get('type')
    if server_type:
        servers = [s for s in servers if s["type"] == server_type]

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

Теперь если пользователь воспользуется фильтром, то URL изменится (например, http://127.0.0.1:5000/servers?type=GPU). Страница покажет только серверы выбранного типа:

Исходная страница с тремя серверами.
До фильтрации.
Страница изменилась, видим отфильтрованные данные — сервер с GPU.
После фильтрации по типу GPU.

Получение параметров из URL

Добавим страницу с подробностями о каждом сервере.

Создадим templates/server_details.html:


    {% extends "base.html" %}

{% block title %}{{ server.name }}{% endblock %}

{% block content %}
    <div class="card border-danger">
        <div class="card-body">
            <h2 class="card-title">{{ server.name }}</h2>
            <p class="card-text">{{ server.desc }}</p>
            <a href="/servers" class="btn btn-danger">← Назад к списку</a>
        </div>
    </div>
{% endblock %}

Дополним app.py:


    @app.route('/server/')
def server_details(server_id):
    servers = {
        1: {"name": "GPU-сервер", "desc": "Идеален для машинного обучения"},
        2: {"name": "Хранилище данных", "desc": "Надежное облачное хранилище"},
        3: {"name": "Вычислительный сервер", "desc": "Мощный сервер для вычислений"}
    }
    
    server = servers.get(server_id)
    if not server:
        return render_template('404.html'), 404

    return render_template('server_details.html', server=server)

Теперь при выборе определенного сервера мы попадаем на страницу с его описанием, например, http://127.0.0.1:5000/server/1:

Страница с описанием сервера с GPU.

Если пользователь введет несуществующий ID, он получит ошибку 404.

Обработка ошибок (404)

Создадим templates/404.html:


    {% extends "base.html" %}

{% block title %}Ошибка 404{% endblock %}

{% block content %}
    <h2>Ошибка 404</h2>
    <p>Тирекс не смог найти эту страницу. Попробуйте вернуться <a href="/">на главную</a>.</p>
{% endblock %}

Добавим обработчик в app.py:


    @app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

После перезагрузки сервера при переходе на несуществующую страницу сайта пользователи будут получать стилизованную ошибку 404 в качестве ответа:

Страница об ошибке 404.

Подключение базы данных

Веб-приложения часто требуют хранения данных, например, пользователей, публикаций, товаров и т. д. Flask поддерживает работу с разными СУБД, но наиболее популярными в веб-разработке  являются SQLite, PostgreSQL и MySQL.

В нашем примере будем использовать PostgreSQL. Для работы с ней во Flask удобен SQLAlchemy — это ORM (Object-Relational Mapping), которая позволяет работать с БД через объекты и классы, избавляя от необходимости писать сложные SQL-запросы вручную.

Установка зависимостей

Установим необходимые пакеты:


    sudo apt install postgresql postgresql-contrib -y

    pip install flask-sqlalchemy psycopg2-binary

После установки запустим сервис postgresql:


    sudo systemctl start postgresql

Настройка подключения к БД

Создадим в корне проекта файл config.py, в котором определим конфигурацию базы данных. Также добавим ключ SECRET_KEY для CSRF-защиты и защиты сессий пользователей:


    import os

class Config:
    SECRET_KEY = 'supersecretkey'
    SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "postgresql://trex:password@localhost/selecteldb")
    SQLALCHEMY_TRACK_MODIFICATIONS = False

Теперь подключим этот конфигурационный файл, изменив начало app.py:


    from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

db = SQLAlchemy(app)

Здесь мы загружаем конфигурацию из config.py и создаем объект db, который связывает приложение с БД.

Определение моделей

Теперь добавим в том же app.py описание модели пользователя:


    class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(256), nullable=False)

    def __repr__(self):
        return f''

Здесь мы создали класс User, который наследуется от db.Model. Когда вы объявляете модель, SQLAlchemy определяет имя таблицы, по умолчанию — это snake_case от имени класса. В нашем случае User → user. Класс автоматически связывается с таблицей, а поля этого класса становятся колонками в базе данных. 

Создание и миграция базы данных

Запустим утилиту psql:


    sudo -u postgres psql

Создадим базу данных:


    CREATE DATABASE selecteldb;
CREATE USER trex WITH ENCRYPTED PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE selecteldb TO trex;

И выйдем из psql:


    exit

Теперь запустим оболочку Python:


    python

И инициализируем базу данных во Flask-приложении:


    from app import app, db


with app.app_context():
    db.create_all()
exit()

В процессе разработки структура базы данных может изменяться: новые поля, таблицы, изменения типов данных. Если использовать db.create_all(), то изменения не применятся к уже существующей базе — придется удалять и пересоздавать таблицы вручную.

Управлять изменениями в БД без потери данных позволяет Flask-Migrate, который добавляет возможность работы с миграциями – системами версионирования схем базы данных.

Установим Flask-Migrate:


    pip install flask-migrate

Актуализируем app.py:


    from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from config import Config
from flask_migrate import Migrate

app = Flask(__name__)
app.config.from_object(Config)

db = SQLAlchemy(app)
migrate = Migrate(app, db)

Теперь выполним инициализацию миграций. Следующая команда создает каталог migrations/, в котором будут храниться файлы с изменениями схемы базы данных:


    flask db init

Следующим шагом создадим миграцию. Flask-Migrate проанализирует текущие модели, зафиксирует изменения и создаст файл с инструкциями для обновления БД.


    flask db migrate -m "Initial migration"

Осталось только применить миграцию. Следующая команда выполнит SQL-запросы для обновления БД без потери данных.


    flask db upgrade

Проверка работы базы данных

Добавим тестового пользователя в БД через консоль Flask:


    flask shell

    from app import db
from app import User

new_user = User(username="testuser", email="test@example.com", password_hash="hashedpassword")
db.session.add(new_user)
db.session.commit()

Проверим, сохранился ли пользователь:


    User.query.all()

Мы увидим следующий ответ:


    []

Введите exit(), чтобы выйти из консоли Flask.

Создание и обработка форм регистрации и авторизации

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

Сессия — это временное хранилище данных о пользователе на стороне сервера. Она удаляется после выхода пользователя или закрытия браузера.

Куки (cookie) — это небольшие файлы, которые хранятся в браузере пользователя и позволяют «запомнить» его даже после закрытия страницы.

Логика работы:

  1. Пользователь регистрируется. Мы сохраняем его данные в БД, но пароли не храним в открытом виде — мы их хешируем.
  2. При входе мы проверяем логин и пароль. Если все верно, создаем сессию и запоминанием ее с использованием куки.

Установка зависимостей

Нам понадобятся несколько библиотек:


    pip install flask-wtf flask-bcrypt flask-login email_validator

Здесь:

  • flask-wtf упрощает работу с формами, добавляет валидацию;
  • flask-bcrypt позволяет безопасно хешировать пароли;
  • flask-login управляет входом, выходом, сессиями и куками;
  • email-validator проверяет корректность заполнения поля «email».

Создаем формы регистрации и входа

Создадим forms.py в корне проекта:


    from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Email, EqualTo, Length

class RegistrationForm(FlaskForm):
    username = StringField('Имя пользователя', validators=[DataRequired(), Length(min=4, max=20)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Пароль', validators=[DataRequired(), Length(min=6)])
    confirm_password = PasswordField('Подтвердите пароль', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Зарегистрироваться')

class LoginForm(FlaskForm):
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Пароль', validators=[DataRequired()])
    remember = BooleanField('Запомнить меня')
    submit = SubmitField('Войти')

Здесь:

  • StringField и PasswordField создают поля формы;
  • validators=[DataRequired()] проверяет, что поля не пустые;
  • EqualTo(‘password’) проверяет, что пароли совпадают;
  • BooleanField(‘Запомнить меня’) добавляет чекбокс, позволяющий пользователю оставаться в системе дольше за счет сохранения сессии в файлах cookie.

Настраиваем авторизацию и сессии

Добавим поддержку пользователей. Создадим файл models.py в корне проекта со следующим содержимым:


    from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin

db = SQLAlchemy()

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(256), nullable=False)

    def __repr__(self):
        return f''

Здесь:

  • UserMixin предоставляет готовые методы для Flask-Login;
  • password_hash хэширует пароль.

Теперь из app.py необходимо удалить описание класса User, так как мы перенесли его в models.py. Актуализируем начало файла:


    from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from config import Config
from flask_migrate import Migrate
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from models import User, db
from forms import RegistrationForm, LoginForm

app = Flask(__name__)
app.config.from_object(Config)

db.init_app(app) 
migrate = Migrate(app, db)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

Здесь:

  • LoginManager отвечает за управление пользователями;
  • @login_manager.user_loader загружает пользователя из БД по user_id.

Создадим шаблоны страниц регистрации и авторизации в каталоге templates/.

Шаблон страницы регистрации register.html:


    {% extends "base.html" %}

{% block content %}
    <div class="container mt-4">
        <h2>Регистрация</h2>
        
            {{ form.hidden_tag() }}
            <div class="mb-3">
                {{ form.username.label(class="form-label") }}
                {{ form.username(class="form-control") }}
            </div>
            <div class="mb-3">
                {{ form.email.label(class="form-label") }}
                {{ form.email(class="form-control") }}
            </div>
            <div class="mb-3">
                {{ form.password.label(class="form-label") }}
                {{ form.password(class="form-control") }}
            </div>
            <div class="mb-3">
                {{ form.confirm_password.label(class="form-label") }}
                {{ form.confirm_password(class="form-control") }}
            </div>
            <button type="submit" class="btn btn-danger">Зарегистрироваться</button>
        
    </div>
{% endblock %}

Шаблон страницы авторизации login.html:


    {% extends "base.html" %}

{% block content %}
    <div class="container mt-4">
        <h2>Вход</h2>
        
            {{ form.hidden_tag() }}
            <div class="mb-3">
                {{ form.email.label(class="form-label") }}
                {{ form.email(class="form-control") }}
            </div>
            <div class="mb-3">
                {{ form.password.label(class="form-label") }}
                {{ form.password(class="form-control") }}
            </div>
            <div class="form-check">
                {{ form.remember(class="form-check-input") }}
                {{ form.remember.label(class="form-check-label") }}
            </div>
            <button type="submit" class="btn btn-danger">Войти</button>
        
    </div>
{% endblock %}

Добавим маршруты регистрации и входа в app.py:


    @app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('lk')) 
    form = RegistrationForm()
    if form.validate_on_submit():
        hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
        user = User(username=form.username.data, email=form.email.data, password_hash=hashed_password)
        db.session.add(user)
        db.session.commit()
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('lk'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first()
        if user and bcrypt.check_password_hash(user.password_hash, form.password.data):
            login_user(user, remember=form.remember.data) 
            return redirect(url_for('lk'))
    return render_template('login.html', form=form)

Маршрут регистрации проверяет, авторизован ли пользователь. Если да, то происходит переадресация на страницу личного кабинета, если нет, то создает форму регистрации. После успешного заполнения пароль хэшируется, данные сохраняются в БД, и выполняется редирект на страницу входа.

В маршруте авторизации проверяется email и пароль. При успешной проверке создается сессия (login_user(user)), а при включенной опции «Запомнить меня» сессия сохраняется в куки на длительный срок.

Для создания личного кабинета добавим шаблон templates/lk.html:


    {% extends "base.html" %}

{% block content %}
    <div class="container mt-4">
        <h2>Личный кабинет</h2>
        <p>Добро пожаловать, {{ current_user.username }}!</p>
        <a href="{{ url_for('logout') }}" class="btn btn-danger">Выйти</a>
    </div>
{% endblock %}

И добавим маршрут в app.py:


    @app.route('/lk)
@login_required 
def lk():
    return render_template(lk.html')

Реализуем выход в app.py:


    @app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

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

Страница регистрации с полями для ввода логина и пароля.

Страница регистрации.

Страница авторизации с полями email и пароля.
Страница авторизации.
Личный кабинет — появляется после авторизации.
Страница личного кабинета.

Настройка внешнего IP-адреса

Каждый раз при выполнении команды python app.py Flask сообщал нам, что он развернул сервер исключительно для целей разработки:


    WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.

В продакшене используют WSGI-серверы, например, Gunicorn — он управляет несколькими процессами и обеспечивает стабильную работу приложения. С помощью него мы запустим веб-приложение и сделаем его доступным извне.

Установите Gunicorn:


    pip install gunicorn

И запустите приложение Flask через него:


    gunicorn -w 4 -b :5000 app:app

Где:

  • -w 4 — количество воркеров (процессов). Flask сам по себе работает в однопоточном режиме, обрабатывая только один запрос за раз. Если их несколько, они ставятся в очередь, что может замедлить работу. Gunicorn позволяет обрабатывать несколько запросов одновременно, используя воркеры;
  • -b <public_ip>:5000 — IP-адрес и порт, по которому будет доступно приложение.

Теперь приложение доступно по публичному IP-адресу устройства, на котором развернут проект.

Вы можете посмотреть код проекта в репозитории на GitHub.

Размещение приложения в облаке Selectel

Разворачивать приложение на локальном сервере — хороший старт, но для стабильной работы и масштабируемости лучше использовать облачные решения. Selectel предлагает надежную инфраструктуру для размещения: от виртуальных машин до управляемых Kubernetes-кластеров.

Облачные серверы Selectel соответствуют 152-ФЗ и обладают сертификатом PCI DSS, что обеспечивает защиту персональных данных и безопасную обработку платежей.

Управлять серверами и интегрировать их с другими сервисами можно через удобную панель управления, с помощью API или Terraform. И вы всегда можете масштабировать ресурсы серверов в ответ на увеличивающуюся нагрузку или оптимизировать под текущие задачи. 

Облачная инфраструктура обеспечивает баланс между производительностью, безопасностью и затратами, позволяя сосредоточиться на развитии функционала и повышении качества сервиса.

Заключение

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