Flask остаётся самым удобным способом собрать небольшое веб-приложение на Python: минимум решений по умолчанию, понятная структура и возможность добавлять только нужные компоненты. В 2026 году он по-прежнему уместен для внутренних сервисов, API, админок и прототипов, которые затем вырастают в рабочий продукт. Ниже — установка, структура проекта, маршруты, шаблоны, работа с базой, тесты и развёртывание под Gunicorn и Nginx.

Что такое Flask и чего в нём нет

Flask — микрофреймворк: он даёт маршрутизацию, шаблонизатор Jinja2, работу с запросами и ответами, сессии и механизм расширений. В нём нет собственного ORM, форм, миграций и админки — всё это подключается отдельно. Такой подход нравится тем, кто не хочет тащить в проект лишние слои, и раздражает тех, кто ждёт готовую структуру как в Django.

Границы приложения задаёт разработчик. Если не продумать структуру заранее, через полгода файл app.py превращается в тысячу строк с маршрутами, запросами к базе и HTML. Поэтому factory-паттерн и blueprints стоит освоить с самого начала.

Установка и каркас проекта

Работайте в виртуальном окружении, чтобы зависимости не смешивались с системным Python. Для продакшена сразу добавьте Gunicorn и драйвер базы данных.

python3 -m venv .venv
. .venv/bin/activate
pip install flask gunicorn flask-sqlalchemy flask-migrate python-dotenv psycopg[binary]
flask --app app run --debug

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

from flask import Flask

def create_app(config=None):
    app = Flask(__name__)
    app.config.from_prefixed_env()
    if config:
        app.config.update(config)

    from views import bp
    app.register_blueprint(bp)
    return app

Файл views.py содержит маршруты, templates — шаблоны, static — CSS и JavaScript. Такое разделение не обязательное, но заметно упрощает сопровождение.

Маршруты, шаблоны и JSON

Blueprint группирует маршруты по смыслу: главная страница, API, административная часть. Декораторы get и post появились в современных версиях Flask и читаются короче, чем methods=[«GET»].

from flask import Blueprint, render_template, jsonify, request

bp = Blueprint("main", __name__)

@bp.get("/")
def index():
    return render_template("index.html", title="Главная")

@bp.get("/api/health")
def health():
    return jsonify(status="ok")

@bp.post("/api/items")
def create_item():
    data = request.get_json(silent=True) or {}
    if not data.get("name"):
        return jsonify(error="name is required"), 400
    return jsonify(id=1, name=data["name"]), 201

Шаблоны наследуются через extends и block. Автоэкранирование включено по умолчанию, поэтому выводить пользовательские строки через |safe без необходимости опасно. Для API используйте jsonify: он выставляет правильный Content-Type и сериализует данные предсказуемо.

Конфигурация и секреты

Настройки читаются из переменных окружения с префиксом FLASK_. Секретный ключ, строка подключения к базе и параметры почты не должны лежать в репозитории. Для локальной разработки используйте файл .env, который добавлен в .gitignore, а на сервере — переменные systemd или менеджера секретов.

export FLASK_SECRET_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')"
export FLASK_SQLALCHEMY_DATABASE_URI="postgresql+psycopg://app:pass@localhost/appdb"
flask --app app routes
flask --app app shell

Команда routes показывает все зарегистрированные адреса и их имена — быстрый способ найти забытый эндпоинт. Shell открывает интерактивную консоль с контекстом приложения, где удобно проверять запросы к базе.

База данных и миграции

Flask-SQLAlchemy даёт удобную обёртку над SQLAlchemy, а Flask-Migrate управляет схемой через Alembic. Модели описываются классами, миграции генерируются автоматически, но результат обязательно просматривают глазами: автогенерация не понимает переименование полей и может предложить удаление данных.

flask --app app db init
flask --app app db migrate -m "add items"
flask --app app db upgrade
flask --app app db current

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

Ошибки, безопасность и типичные промахи

В продакшене отключите debug: интерактивный отладчик позволяет выполнять код на сервере. Обработчики ошибок должны возвращать аккуратные страницы и не раскрывать стек вызовов.

@app.errorhandler(404)
def not_found(error):
    return render_template("404.html"), 404

@app.after_request
def security_headers(response):
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "SAMEORIGIN"
    return response

Добавьте ограничение размера запроса через MAX_CONTENT_LENGTH, CSRF-защиту для форм через Flask-WTF и ограничение частоты запросов для публичных эндпоинтов. Если приложение стоит за reverse proxy, настройте ProxyFix, иначе Flask будет видеть внутренний IP и неверную схему.

  • Секретный ключ захардкожен в коде — частая причина утечки сессий.
  • Отладочный сервер оставлен в продакшене — низкая производительность и риск RCE.
  • Миграции применяются вручную без резервной копии — риск потери данных.
  • Тяжёлые задачи выполняются в обработчике запроса — таймауты и зависания.

Тесты

Тестовый клиент Flask не поднимает сетевой сервер и работает быстро. Фикстура создаёт приложение с временной базой, а каждый тест получает чистый контекст.

import pytest
from app import create_app

@pytest.fixture
def client():
    app = create_app({"TESTING": True, "SQLALCHEMY_DATABASE_URI": "sqlite://"})
    with app.test_client() as client:
        yield client

def test_health(client):
    response = client.get("/api/health")
    assert response.status_code == 200
    assert response.get_json()["status"] == "ok"

Запускайте pytest -q в CI и не забывайте про проверку прав доступа: тест, который дергает админский эндпоинт без авторизации, должен получать 401 или 403.

Развёртывание

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

gunicorn -w 4 -b 127.0.0.1:8000 "app:create_app()"
systemctl status flaskapp
journalctl -u flaskapp -n 50 --no-pager
curl -i http://127.0.0.1:8000/api/health

Nginx принимает внешние соединения, отдаёт статику и проксирует запросы на localhost. Таймауты и размер тела запроса должны соответствовать возможностям приложения, иначе пользователи увидят 502 или 413.

server {
    listen 80;
    server_name app.example.ru;
    location /static/ { root /srv/flaskapp; }
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Для диагностики смотрите журнал Gunicorn и systemd, проверяйте, что виртуальное окружение активировано в unit-файле, а рабочая директория указана явно. Ошибка «ModuleNotFoundError» почти всегда означает неверный путь или запуск не тем интерпретатором.

Итог

Flask хорош там, где нужен контроль над структурой и минимум магии. Начните с фабрики приложения и blueprints, вынесите настройки в окружение, подключите миграции и тесты, а на сервер ставьте Gunicorn за Nginx. Главные правила: никакого debug в продакшене, секреты вне репозитория, резервная копия перед миграцией и явная проверка прав доступа.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте