Веб-разработка на Flask + AI-прототипирование

Курс знакомит с основами веб-разработки и созданием приложений на Flask: от HTTP и роутинга до API и работы с базами данных. Отдельный блок посвящён AI-инструментам для ускоренного прототипирования, а финальная часть — тестированию и базовой архитектуре проектов.

1. Основы веба: HTTP, REST и старт во Flask

Основы веба: HTTP, REST и старт во Flask

Этот курс посвящён веб-разработке на Flask и ускорению работы через AI-прототипирование. Но прежде чем генерировать код и собирать приложения, важно понять базовые правила игры: как клиент общается с сервером, что такое HTTP и как проектировать API в стиле REST. В конце статьи вы запустите первый минимальный проект на Flask.

Как устроен веб

Веб-приложение почти всегда состоит из двух сторон:

  • Клиент: обычно браузер (или мобильное приложение, или другой сервис), который отправляет запросы и показывает результат.
  • Сервер: приложение, которое принимает запросы, выполняет логику, работает с данными и возвращает ответ.
  • Ключевая идея: клиент и сервер общаются по сети по стандартному протоколу.

    !Базовая схема общения клиента и сервера по HTTP и обращение сервера к базе данных

    HTTP простыми словами

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

    Полезные справочники:

  • Обзор HTTP на MDN
  • Методы HTTP на MDN
  • Коды ответа HTTP на MDN
  • Из чего состоит HTTP-запрос

    Обычно в запросе есть:

  • Метод: что хотим сделать (например, GET, POST).
  • URL: к чему обращаемся (например, /users/42).
  • Заголовки: метаданные (например, тип данных, авторизация).
  • Тело запроса: данные, которые отправляем на сервер (часто в POST, PUT, PATCH).
  • Пример «как выглядит идея запроса»:

    Из чего состоит HTTP-ответ

    Ответ обычно включает:

  • Код статуса: результат обработки запроса.
  • Заголовки: метаданные ответа (например, Content-Type).
  • Тело ответа: данные (HTML, JSON, файл).
  • Пример:

    !Анатомия HTTP-запроса и HTTP-ответа

    Основные HTTP-методы

  • GET: получить данные (например, список задач).
  • POST: создать ресурс или запустить действие (например, создать задачу).
  • PUT: заменить ресурс целиком (например, полностью обновить профиль).
  • PATCH: частично обновить ресурс.
  • DELETE: удалить ресурс.
  • Важно: в реальной практике бывают исключения, но в REST-подходе обычно стремятся, чтобы смысл методов сохранялся.

    Коды ответа, которые встречаются чаще всего

  • 200 OK: запрос успешен.
  • 201 Created: ресурс создан (часто после POST).
  • 204 No Content: успешно, но тело ответа не нужно.
  • 400 Bad Request: некорректный запрос (например, не прошла валидация).
  • 401 Unauthorized: нужна аутентификация.
  • 403 Forbidden: доступ запрещён.
  • 404 Not Found: ресурс не найден.
  • 500 Internal Server Error: ошибка на стороне сервера.
  • Заголовки и форматы данных

    Часто встречающиеся заголовки:

  • Content-Type: формат данных в теле (например, application/json).
  • Accept: какой формат ответа ожидает клиент.
  • Authorization: данные для авторизации.
  • Популярные форматы:

  • HTML: для отображения страниц в браузере.
  • JSON: основной формат для API.
  • Form-data / x-www-form-urlencoded: часто используется в HTML-формах.
  • API и REST

    API — это интерфейс, через который одна программа общается с другой. В вебе это обычно набор HTTP-эндпоинтов.

    REST — популярный стиль проектирования API, где акцент на ресурсах и предсказуемых правилах.

    Ресурсы и адреса

    В REST часто мыслят существительными:

  • /users — коллекция пользователей
  • /users/42 — конкретный пользователь
  • /tasks — коллекция задач
  • /tasks/10 — конкретная задача
  • Вместо «глагольных» адресов вроде /createUser обычно используют сочетание адреса ресурса и HTTP-метода.

    !Как методы HTTP сочетаются с URL ресурсов в REST

    Мини-набор практических правил REST

  • Один ресурс — один «понятный» URL.
  • Поведение определяется HTTP-методом.
  • Сервер возвращает корректные коды статуса.
  • Формат данных в API чаще всего JSON.
  • Первый старт во Flask

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

    Официальная документация:

  • Flask Quickstart
  • Установка и виртуальное окружение

  • Создайте папку проекта.
  • Создайте виртуальное окружение.
  • Установите Flask.
  • Пример команд:

    Минимальное приложение

    Создайте файл app.py:

    Запустите:

    Откройте в браузере адрес, который появится в консоли (обычно http://127.0.0.1:5000/).

    Что такое роутинг

    Роут (маршрут) связывает:

  • путь (например, / или /users/42)
  • HTTP-метод (например, GET)
  • функцию-обработчик, которая формирует ответ
  • В Flask это задаётся декоратором, например @app.get("/path").

    Возврат JSON (заготовка под API)

    Flask умеет удобно возвращать JSON через jsonify:

    Здесь вы уже делаете небольшой API-эндпоинт. В следующих статьях мы расширим это до полноценного REST API.

    Как проверять эндпоинты без браузера

    Для API удобно использовать curl:

    Это привычный способ быстро проверять ответы, коды статуса и JSON.

    Как этот материал связан с тем, что будет дальше

  • Вы узнали базовую механику HTTP и смысл методов и статус-кодов: это основа для роутинга и обработки запросов.
  • Вы познакомились с REST-подходом: он поможет проектировать предсказуемые API.
  • Вы запустили минимальный Flask-проект: на следующем шаге мы добавим шаблоны, формы и начнём строить структуру приложения.
  • В следующих статьях курса мы перейдём к практической разработке на Flask: обработка параметров запроса, шаблоны, формы, затем подключим базу данных PostgreSQL и SQLAlchemy, а позже обсудим, как ускорять прототипирование с помощью AI-инструментов и как тестировать приложение.

    2. Flask в практике: роутинг, шаблоны, формы и API

    Flask в практике: роутинг, шаблоны, формы и API

    В прошлой статье вы разобрали клиент-серверную модель, HTTP-методы, коды статуса и запустили минимальное Flask-приложение. Теперь перейдём к ежедневной практике: научимся уверенно писать роуты, принимать данные из запроса, отдавать ответы разного типа, рендерить шаблоны, обрабатывать отправку форм и собирать небольшой API.

    Роутинг во Flask

    Роутинг связывает URL и HTTP-метод с Python-функцией, которая возвращает ответ. В Flask это задаётся декораторами @app.get, @app.post и другими.

    Документация:

  • Flask Quickstart: Routing
  • Базовые роуты и методы

    Ключевая идея: один и тот же путь может иметь разные обработчики для разных методов.

    Параметры в пути

    Часто часть пути является данными, например идентификатор пользователя. Во Flask это делается через переменные в URL.

    Здесь:

  • <int:user_id> означает: извлечь значение из URL, преобразовать в целое число и передать в функцию как аргумент user_id
  • Полезно помнить, что преобразователь (int) защищает от части некорректных запросов: если пришло не число, роут не совпадёт и клиент получит 404.

    Параметры запроса в URL

    Есть второй способ передать данные: query-параметры (после ?), например /search?q=flask&page=2. Во Flask они доступны в request.args.

    !Как Flask разбирает path-параметры и query-параметры

    Объект request: откуда брать данные запроса

    Flask предоставляет объект request, через который вы получаете данные, пришедшие от клиента.

    Документация:

  • Flask API: Incoming Request Data
  • Основные источники данных

    | Что нужно получить | Где это лежит во Flask | Когда используется | | --- | --- | --- | | Query-параметры ?a=1 | request.args | фильтры, поиск, пагинация | | Данные формы | request.form | классические веб-формы | | JSON-тело запроса | request.get_json() | API и фронтенд-приложения | | Заголовки | request.headers | авторизация, формат данных |

    Пример чтения query-параметров:

    Возврат кода статуса

    Вы уже знаете коды статуса HTTP. Во Flask их можно задавать явно, возвращая кортеж (тело, код).

    Справочник по кодам:

  • MDN: HTTP response status codes
  • Шаблоны: как формировать страницы

    Шаблон — это файл с текстом, в который подставляются данные. Во Flask используется движок Jinja, а рендеринг выполняет render_template.

    Документация:

  • Flask: Templates
  • Обычно шаблоны создают HTML-страницы, но принцип одинаков и для текстовых шаблонов. Ниже пример именно текстового шаблона (чтобы сфокусироваться на механике подстановки).

    Минимальный пример render_template

    Структура проекта:

  • app.py
  • templates/
  • - hello.txt

    app.py:

    templates/hello.txt:

    Здесь:

  • render_template("hello.txt", name=name) передаёт в шаблон переменную name
  • {{ name }} в шаблоне подставляет значение
  • Условия и циклы в шаблонах

    templates/tasks.txt:

    Этот подход часто используется, чтобы отделить:

  • логику обработки запроса (Python)
  • представление результата (шаблон)
  • Формы: как принимать данные от пользователя

    Форма — это способ отправить данные с клиента на сервер (классически из браузера). Важно различать:

  • GET обычно используют, когда вы получаете данные (например, поиск)
  • POST — когда вы создаёте/изменяете данные на сервере
  • Введение в формы:

  • MDN: Your first form
  • Приём данных формы во Flask

    Если клиент отправляет данные как application/x-www-form-urlencoded, то во Flask это будет доступно через request.form.

    Проверить такой эндпоинт можно без браузера через curl:

    Паттерн Post/Redirect/Get

    После успешного POST в веб-приложениях часто делают редирект на страницу списка или карточки сущности. Это уменьшает риск повторной отправки данных при обновлении страницы.

    Документация:

  • Flask: redirect
  • Flask: url_for
  • !Зачем нужен Post/Redirect/Get после отправки формы

    Собственное API: JSON, ошибки и статусы

    API в контексте Flask — это набор эндпоинтов, которые возвращают данные (чаще JSON), а не страницу.

    Возврат JSON

    Лучше использовать jsonify: он корректно выставляет Content-Type и сериализует данные.

    Документация:

  • Flask: jsonify
  • JSON на вход: request.get_json

    Если клиент присылает application/json, обычно читают так:

    Смысл silent=True: если пришёл невалидный JSON, Flask не выбросит исключение автоматически, а вернёт None, и вы сможете вернуть понятную ошибку.

    Единый стиль ошибок

    Хорошая практика для API:

  • всегда возвращать JSON даже в ошибках
  • использовать корректные коды статуса (400, 404, 422, 500 по ситуации)
  • Пример обработчика 404 для API-роутов:

    Важно: такой обработчик сработает на все 404 в приложении. В реальном проекте часто разделяют API и страницы (например, разными префиксами /api/...) и аккуратно настраивают обработку ошибок.

    Мини-проект в одной папке: страницы + форма + API

    Ниже — компактный пример, который объединяет всё из статьи:

  • текстовая “страница” со списком задач через шаблон
  • создание задачи через POST формы
  • API для списка и создания задач через JSON
  • Структура:

  • app.py
  • templates/
  • - tasks.txt

    app.py:

    templates/tasks.txt:

    Команды для проверки:

    Как это связано с дальнейшим курсом

  • Сейчас вы научились строить слой веба: роуты, запросы, ответы, шаблоны, формы и API.
  • В следующих шагах курса появится база данных (PostgreSQL и SQLAlchemy), чтобы задачи сохранялись не в памяти процесса.
  • Когда начнёте ускорять разработку через AI-инструменты (Cursor, Copilot и другие), именно эти “строительные блоки” вы будете генерировать и править чаще всего: каркас роутов, контракты API, валидацию и обработку ошибок. Важно понимать, что именно вы просите сгенерировать и как проверить корректность результата (методы, статусы, формат данных).
  • 3. Базы данных: PostgreSQL, SQLAlchemy и ORM-модели

    Базы данных: PostgreSQL, SQLAlchemy и ORM-модели

    В прошлых статьях вы научились принимать HTTP-запросы во Flask, обрабатывать формы и возвращать JSON в API. Но пока данные жили в памяти процесса (в списке TASKS), а значит:

  • после перезапуска сервера всё пропадает
  • невозможно безопасно работать с данными из нескольких процессов
  • сложно искать, фильтровать, связывать сущности
  • В этой статье вы подключите настоящую базу данных PostgreSQL, разберёте базовые идеи SQLAlchemy и научитесь описывать данные через ORM-модели, чтобы затем использовать их в ваших Flask-роутах и API.

    !Как Flask-приложение общается с PostgreSQL через SQLAlchemy

    Что такое база данных и почему PostgreSQL

    База данных в веб-приложении — это отдельный сервис, который надёжно хранит данные и умеет быстро отвечать на запросы к ним.

    PostgreSQL — популярная реляционная база данных. Она хорошо подходит для веб-приложений, потому что:

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

  • Документация PostgreSQL
  • Загрузка PostgreSQL
  • Реляционная модель простыми словами

    В реляционных БД данные хранятся в таблицах.

  • строка — один объект (например, одна задача)
  • столбец — одно поле (например, title)
  • первичный ключ (primary key) — уникальный идентификатор строки (часто id)
  • Пример таблицы задач (идея):

  • tasks
  • - id (число, уникально) - title (текст) - is_done (логическое значение)

    SQL: минимальный набор, который реально нужен

    SQL — язык запросов к реляционным базам. Даже если вы используете ORM, полезно понимать базовые операции.

  • SELECT — получить данные
  • INSERT — добавить строку
  • UPDATE — изменить строки
  • DELETE — удалить строки
  • Примерный смысл запросов:

    Важно: в реальных приложениях запросы должны быть параметризованными, чтобы не допускать SQL-инъекции. ORM и корректное использование драйверов как раз помогают не собирать SQL строками вручную.

    Подключение PostgreSQL: пользователь, база, строка подключения

    Чтобы приложение подключилось к PostgreSQL, вам нужна строка подключения. В типичном виде она выглядит так:

    postgresql+psycopg://USER:PASSWORD@HOST:PORT/DBNAME

  • USER — пользователь БД
  • PASSWORD — пароль
  • HOST — адрес сервера (локально часто localhost)
  • PORT — порт (обычно 5432)
  • DBNAME — имя базы данных
  • Дальше вы передадите эту строку в SQLAlchemy.

    SQLAlchemy: что это и из каких частей состоит

    SQLAlchemy — библиотека для работы с БД из Python. Она позволяет:

  • описывать таблицы как Python-классы (ORM)
  • выполнять запросы через Python-объекты
  • управлять соединениями и транзакциями
  • Официальная документация:

  • Документация SQLAlchemy
  • Три ключевых понятия

  • Engine — “точка входа” в базу: управляет подключениями
  • Session — рабочий объект, через который вы читаете и записываете данные
  • Модели (ORM) — классы, которые сопоставляются таблицам
  • !Как ORM-модель сопоставляется таблице в PostgreSQL

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

    Для PostgreSQL вам нужен драйвер. Современный вариант для SQLAlchemy — psycopg.

    Документация драйвера:

  • Документация psycopg
  • Установка:

    Если вы планируете подключаться к PostgreSQL на Windows и столкнётесь с проблемами сборки, иногда помогает установка бинарного пакета:

    Мини-проект: сохраняем задачи в PostgreSQL через ORM

    Ниже вы соберёте минимальную структуру:

  • db.py — подключение SQLAlchemy и фабрика сессий
  • models.py — ORM-модели
  • app.py — Flask-приложение и роуты
  • Шаг 1. db.py: engine и Session

    Создайте файл db.py:

    Что здесь происходит:

  • DATABASE_URL читается из переменных окружения, чтобы не хранить секреты в коде
  • ENGINE создаётся один раз на процесс
  • SessionLocal — фабрика, которая создаёт Session для каждого запроса/операции
  • Шаг 2. models.py: объявляем ORM-модель

    Создайте models.py:

    Разбор терминов:

  • __tablename__ — имя таблицы в БД
  • mapped_column(...) — описание столбца
  • primary_key=True — первичный ключ
  • nullable=False — поле обязательно
  • default=False — значение по умолчанию на уровне ORM
  • Метод to_dict пригодится, чтобы возвращать JSON в API.

    Шаг 3. Создаём таблицы

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

    Добавьте файл init_db.py:

    Запустите:

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

    Шаг 4. app.py: Flask + CRUD с транзакциями

    Создайте app.py:

    Что важно понять в этом коде:

  • with get_session() as session: создаёт сессию и закрывает её после блока
  • session.add(...) добавляет объект в “набор изменений”
  • session.commit() фиксирует транзакцию (изменения реально записываются в БД)
  • session.refresh(task) подтягивает значения, которые БД могла проставить сама (например, id)
  • session.get(Task, task_id) — быстрый способ получить по первичному ключу
  • Проверка через curl:

    Транзакции: почему commit так важен

    Транзакция — это “пакет” изменений, который либо применится целиком, либо не применится вообще.

    Практический смысл:

  • без commit() новые данные не сохранятся
  • если в середине операции случилась ошибка, вы не получите “полусохранённое” состояние
  • В более крупных обработчиках полезно делать так:

  • если всё ок: commit()
  • если ошибка: rollback()
  • В нашем примере при исключении внутри блока стоит явно добавить обработку ошибок. Учебный минимум можно оставить как есть, но в продакшене транзакции всегда продумывают.

    Схема данных: ограничения, индексы, связи

    Когда приложение растёт, вы добавляете правила на уровне базы:

  • ограничения (constraints)
  • - nullable=False — поле обязательно - unique=True — значение уникально
  • индексы — ускоряют поиск и фильтрацию
  • связи (relationships) — например “у пользователя много задач”
  • В этой статье мы не углубляемся в связи, чтобы закрепить базовый CRUD. Но дальше, когда появятся “пользователи”, “проекты”, “комментарии”, без связей будет сложно.

    Документация по ORM:

  • SQLAlchemy ORM
  • Миграции: как менять структуру таблиц без ручного SQL

    Когда вы меняете модели (добавляете поле, меняете тип), базу данных тоже нужно обновить. Делать это вручную опасно.

    Для миграций в экосистеме SQLAlchemy чаще всего используют Alembic:

  • Документация Alembic
  • Идея миграций:

  • вы описываете изменение схемы как отдельный “шаг”
  • шаги применяются последовательно
  • можно воспроизводимо обновлять БД на разных окружениях
  • В рамках курса миграции логично подключать после того, как вы уверенно делаете CRUD и понимаете, как устроены модели.

    Как это связано с AI-прототипированием

    AI-инструменты хорошо ускоряют работу с повторяющимся кодом вокруг БД:

  • генерация моделей по описанию сущностей
  • наброски CRUD-роутов
  • шаблоны для валидации входных данных
  • Но важно проверять результат по чек-листу:

  • есть ли commit() там, где должны сохраняться изменения
  • корректны ли коды статусов (201 при создании, 204 при удалении)
  • не допускается ли “пустой” title и другие некорректные состояния
  • не смешаны ли “страницы” и “API” в обработке ошибок (HTML vs JSON)
  • Итог

    Теперь у вас есть полноценная цепочка:

  • HTTP-запросы приходят во Flask
  • обработчики читают данные (request.get_json)
  • данные сохраняются в PostgreSQL через SQLAlchemy (Session + ORM)
  • API возвращает JSON
  • Дальше по курсу эта база позволит:

  • добавлять сущности и связи
  • вводить аутентификацию и права доступа
  • покрывать логику тестами
  • быстрее прототипировать новые фичи, не теряя контроль над качеством
  • 4. AI-прототипирование: генерация кода и быстрые итерации

    AI-прототипирование: генерация кода и быстрые итерации

    В прошлых статьях вы прошли базовую линию веб-разработки на Flask: HTTP и REST, роутинг и обработку запросов, шаблоны и формы, а затем подключили PostgreSQL через SQLAlchemy и сделали CRUD в API. Теперь у вас есть все строительные блоки, чтобы собирать реальные мини-приложения.

    Следующий шаг курса — научиться ускорять разработку с помощью AI-инструментов (например, GitHub Copilot, Cursor и аналогов), но не теряя контроля над качеством: корректностью HTTP-контрактов, транзакций, обработки ошибок и структуры проекта.

    Что такое AI-прототипирование

    AI-прототипирование — это подход, при котором вы используете AI-ассистента для быстрого получения черновика решения:

  • каркаса роутов и эндпоинтов
  • моделей SQLAlchemy
  • базовых CRUD-операций
  • валидации входных данных
  • тестов и моков
  • документации и примеров запросов
  • Важно понимать границу ответственности:

  • AI ускоряет создание заготовок и помогает с рутинными кусками кода
  • вы отвечаете за корректность, безопасность, архитектуру и итоговый результат
  • Когда AI особенно полезен во Flask-проектах

    AI-ассистент даёт максимальную пользу там, где много повторяющихся паттернов.

  • Генерация однотипных CRUD-эндпоинтов для новых сущностей
  • Быстрое добавление валидации и единых ошибок API
  • Рефакторинг: вынесение повторяющегося кода, переименование, разбиение на модули
  • Генерация тестов, которые проверяют контракт API
  • Объяснение ошибок (traceback), поиск причины, предложение минимального исправления
  • А вот в местах с высоким риском (аутентификация, права доступа, платёжная логика) AI тоже помогает, но только если вы делаете строгую проверку и постепенно вводите изменения.

    Базовый цикл быстрых итераций

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

    !Цикл разработки маленькими шагами с проверкой результата

    Практический алгоритм:

  • Зафиксируйте минимальный результат итерации
  • Опишите контракт: вход, выход, статусы ошибок
  • Дайте AI контекст проекта и ограничения
  • Вставьте код в проект и запустите
  • Проверьте руками и через тесты
  • Сделайте небольшой рефакторинг и только потом идите дальше
  • Ключевая дисциплина: одна итерация должна быть маленькой. Если вы просите AI сразу «сделать весь проект», вы получите много кода, который сложно проверить.

    Как писать промпты, чтобы код был полезным

    Хороший промпт — это техническое задание на один шаг.

    Мини-шаблон промпта

    Ниже структура, которую удобно копировать и заполнять.

    Что здесь важно:

  • Контекст проекта: чтобы AI не придумывал другую архитектуру
  • Список конкретных изменений: что добавить и куда
  • Требования к контракту: форматы, статусы, стиль ошибок
  • Примеры запросов и ответов: снижают вероятность «не того JSON»
  • Частые ошибки в промптах

  • «Сделай красиво и правильно» без критериев
  • Отсутствие указания, где хранится get_session() и как вы работаете с транзакциями
  • Нет требований к статус-кодам и формату ошибок
  • Просьба «добавь авторизацию» без описания модели угроз и требований
  • Пример: добавляем новую сущность через AI, но контролируем качество

    Допустим, после задач (Task) вы хотите добавить проекты (Project). Это типичный кейс: новая ORM-модель + пара эндпоинтов.

    Шаг изменения моделей

    Цель: добавить модель Project в models.py.

    Пример результата, к которому вы хотите прийти:

    Проверка после генерации:

  • модель действительно добавлена в тот же Base
  • nullable=False стоит там, где поле обязательное
  • есть метод to_dict() для сериализации в JSON
  • После этого не забудьте создать таблицы. В учебном режиме можно так же, как раньше, через Base.metadata.create_all(ENGINE).

    Шаг изменения роутов

    Цель: добавить GET /api/projects и POST /api/projects в app.py.

    Пример результата:

    Проверка через curl:

    Что вы контролируете как разработчик:

  • есть ли session.commit() в местах записи
  • используется ли session.refresh() для получения id
  • соответствует ли формат ответа контракту
  • корректны ли статусы 201 и 400
  • Чек-лист проверки сгенерированного кода

    AI может сгенерировать код, который выглядит правдоподобно, но ломается в деталях. Ниже минимальный чек-лист, который стоит прогонять по каждому PR или итерации.

    | Область | Что проверить | Типичные ошибки AI | | --- | --- | --- | | HTTP-контракт | Метод, URL, статусы, формат JSON | Возвращает 200 вместо 201, смешивает HTML и JSON | | Валидация | Обязательные поля, пустые строки, типы | Принимает пустой title, не проверяет JSON | | SQLAlchemy | Сессия, commit, refresh, select | Забывает commit(), создаёт сессию глобально | | Ошибки | Единый формат {error: '...'} | Возвращает текст вместо JSON | | Структура | Файлы и импорты совпадают с проектом | Придумывает новые модули и «магические» функции | | Безопасность | Секреты, инъекции, доступы | Хардкодит пароли, советует собирать SQL строками |

    Если чек-лист не проходит, не пытайтесь «допинать» проблему вручную вслепую. Лучше сделать новую итерацию: точечно переписать промпт и попросить AI исправить конкретный дефект.

    Как просить AI исправлять баги, а не переписывать всё

    Когда что-то не работает, цель — получить минимальный патч, а не новый проект.

    Шаблон промпта на отладку:

    Часто причина в одном из пунктов:

  • нет commit()
  • транзакция откатывается из-за исключения
  • используется другая база или другая строка подключения
  • данные не читаются из JSON из-за неверного Content-Type
  • Быстрое прототипирование API через примеры

    Чтобы итерации были быстрыми, удобно начинать не с кода, а с примеров. Это снижает число недопониманий между вами и AI.

    Пример: вы хотите эндпоинт обновления задачи.

  • Запрос: PATCH /api/tasks/10
  • JSON: {'is_done': true}
  • Успех: 200 и объект задачи
  • Если задачи нет: 404 и {'error': 'not found'}
  • После фиксации примеров вы просите AI «реализовать ровно это». Так вы держите контроль над контрактом, а AI ускоряет механику.

    AI и поддерживаемость: как не превратить проект в набор фрагментов

    У AI есть характерная ловушка: он легко генерирует много кода, но сложнее поддерживать единый стиль.

    Чтобы прототип стал нормальной базой проекта:

  • Договоритесь о структуре (например, app.py, db.py, models.py, позже services.py)
  • Держите единый стиль ошибок для API
  • Старайтесь выносить повторяющуюся логику из роутов
  • Добавляйте тесты на контракт (это снижает риск регрессий при следующих итерациях)
  • Если вы просите AI сделать рефакторинг, задавайте границы:

  • что можно менять
  • что трогать нельзя (например, публичные URL)
  • какие тесты должны остаться зелёными
  • Как это связывается с предыдущими темами курса

  • Из статьи про HTTP и REST вы берёте основу для правильных методов, URL и кодов статуса
  • Из практики Flask вы используете навыки работы с request, jsonify, обработкой ошибок
  • Из статьи про PostgreSQL и SQLAlchemy вы контролируете транзакции и жизненный цикл Session
  • AI-прототипирование ускоряет сборку этих блоков, но качество всё равно определяется тем, насколько хорошо вы понимаете основы.

    Итог

    AI-инструменты в веб-разработке на Flask — это ускоритель итераций:

  • они быстро создают каркас решения
  • помогают с рутиной (CRUD, валидация, тесты)
  • ускоряют отладку
  • Но рабочий процесс остаётся инженерным:

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

    5. Тестирование и архитектура: pytest, монолит, микросервисы

    Тестирование и архитектура: pytest, монолит, микросервисы

    В предыдущих статьях вы построили Flask-приложение с API и подключили PostgreSQL через SQLAlchemy, а затем обсудили, как ускорять разработку через AI-прототипирование. Теперь важный шаг к проектам, которые можно поддерживать: научиться проверять поведение автоматически и понимать, как выбирать архитектуру.

    В этой статье вы разберёте:

  • тестирование на Python с pytest: что именно тестировать и как
  • тестирование Flask API через встроенный тест-клиент
  • тестирование слоя БД: как делать тесты воспроизводимыми
  • архитектуру: чем монолит отличается от микросервисов, и когда что выбирать
  • Зачем тесты в Flask-проекте

    Когда вы быстро прототипируете функциональность (особенно с помощью AI), риск регрессий растёт: вы добавили «маленькую» фичу, а сломали код статусов, валидацию или транзакции.

    Тесты дают две ключевые выгоды:

  • контроль контракта API: методы, URL, JSON-формат, коды статуса
  • безопасный рефакторинг: можно менять внутренности, не ломая внешнее поведение
  • !Пирамида показывает, каких тестов обычно должно быть больше и почему

    Какие тесты бывают в веб-приложении

    Ниже практическая классификация для Flask API.

    | Уровень | Что проверяем | Пример | Плюсы | Минусы | | --- | --- | --- | --- | --- | | Unit-тесты | Чистую бизнес-логику без Flask и без БД | функция валидации, расчёт статусов | очень быстрые | не ловят ошибки интеграции | | Интеграционные | Flask-роуты, сериализацию JSON, БД | POST /api/tasks создаёт запись | ловят реальные баги системы | медленнее, сложнее окружение | | E2E | Поведение почти как в продакшене | запросы по сети к поднятому сервису | максимальная уверенность | самые дорогие по времени |

    В учебном Flask-проекте обычно достаточно:

  • много unit-тестов для логики
  • несколько интеграционных тестов для ключевых эндпоинтов
  • Почему pytest, и что нужно установить

    pytest популярен за счёт:

  • лаконичных тестов на обычных assert
  • удобных fixtures для подготовки окружения
  • хорошей экосистемы плагинов
  • Ссылки:

  • Документация pytest
  • Тестирование во Flask
  • Установка:

    Структура проекта (один из простых вариантов):

  • app.py
  • db.py
  • models.py
  • tests/
  • - test_tasks_api.py

    Запуск тестов:

    Подготовка приложения к тестированию

    Если у вас всё в одном app.py, тестировать можно, но удобнее слегка улучшить структуру.

    Идея: фабрика приложения

    Фабрика приложения создаёт Flask-приложение как функцию, чтобы тесты могли:

  • включать TESTING
  • подменять конфигурацию
  • создавать изолированное приложение для каждого набора тестов
  • Пример минимальной фабрики (упрощённо):

    Это не единственно возможный подход, но он хорошо масштабируется, когда тестов становится много.

    Тестирование Flask API через test client

    Flask предоставляет встроенный тест-клиент, который позволяет делать запросы без поднятия сервера.

    Ссылка:

  • Flask test client
  • Минимальный тест на health-check

    Здесь важно:

  • client.get(...) возвращает объект ответа
  • resp.get_json() удобно разбирает JSON
  • мы явно проверяем контракт: и статус, и тело
  • Тесты для CRUD: что проверять в первую очередь

    Для эндпоинта создания задачи (POST /api/tasks) минимальный полезный набор проверок:

  • корректный статус 201
  • в ответе есть id, title, is_done
  • валидация пустого title возвращает 400 и JSON с ошибкой
  • Пример (без привязки к реальной БД, только демонстрация контракта):

    Но чтобы тестировать создание «по-настоящему», нужно решить вопрос с базой.

    Интеграционные тесты с базой: практичные стратегии

    Есть несколько способов тестировать Flask + SQLAlchemy.

    Стратегия: отдельная тестовая БД

    Идея:

  • создаёте отдельную БД, например flask_ai_course_test
  • тесты используют отдельный DATABASE_URL
  • перед прогоном тестов очищаете таблицы или используете транзакции
  • Плюсы:

  • поведение ближе к продакшену (PostgreSQL остаётся PostgreSQL)
  • Минусы:

  • нужно поднять Postgres локально или в CI
  • Стратегия: транзакция на тест и откат

    Если вы делаете запись в БД, после теста её нужно убрать. Частый паттерн:

  • в начале теста открыть транзакцию
  • в конце сделать rollback()
  • В SQLAlchemy это реализуется по-разному в зависимости от того, как устроено создание Session. Главное правило: тесты должны быть изолированными.

    Почему важно не хранить сессию глобально

    Если AI сгенерировал код, где session = SessionLocal() лежит глобально и переиспользуется, тесты часто становятся нестабильными.

    Хорошая практика для Flask:

  • создавать Session на короткий срок
  • закрывать её сразу после операции
  • Именно поэтому ранее в курсе использовался паттерн with get_session() as session:.

    Упрощённый приём для курса: подмена get_session в тестах

    Чтобы не строить сложную инфраструктуру, можно применить учебный приём: в тестах подменять get_session() на версию, которая подключается к тестовой БД.

    Идея:

  • приложение импортирует get_session из db.py
  • в тестах через monkeypatch (fixture pytest) подменяем функцию
  • Ссылка:

  • pytest monkeypatch
  • Важное ограничение: этот подход требует аккуратности с импортами и хорошо работает, если архитектура проекта предсказуемая.

    Где заканчивается pytest и начинается unittest

    В Python есть встроенный модуль unittest.

    Ссылка:

  • Документация unittest
  • Практически:

  • unittest полезен, когда вы работаете в средах, где он принят по стандарту
  • pytest обычно быстрее даёт результат и проще для старта
  • В этом курсе основной акцент на pytest, потому что он хорошо сочетается с быстрыми итерациями и AI-прототипированием: вы легко генерируете тесты и быстро проверяете, что контракт не сломан.

    Архитектура: монолит и микросервисы простыми словами

    Архитектура отвечает на вопрос: как организовать код и развертывание системы, чтобы её было удобно развивать.

    Монолит

    Монолит обычно означает:

  • одно приложение
  • один процесс или несколько процессов одного приложения
  • одна кодовая база, одно развёртывание
  • Важно: монолит не равен «хаос в одном файле». Монолит может быть модульным и аккуратным.

    Микросервисы

    Микросервисы означают:

  • несколько независимых сервисов
  • каждый сервис отвечает за свою бизнес-область
  • сервисы общаются по сети (HTTP, очереди)
  • отдельные развёртывания
  • !Схема показывает, что меняется при переходе от монолита к микросервисам

    Как выбрать: сравнение по критериям

    | Критерий | Монолит | Микросервисы | | --- | --- | --- | | Старт проекта | проще | сложнее | | Развёртывание | одно | много | | Тестирование | проще (меньше сетевых границ) | сложнее (контракты, интеграции) | | Масштабирование | часто масштабируют целиком | можно масштабировать сервисы отдельно | | Команды | удобно для небольшой команды | удобно для многих автономных команд | | Риск «распила» | низкий | высокий при неправильных границах |

    Дополнительное чтение:

  • Microservices от Martin Fowler
  • MonolithFirst от Martin Fowler
  • Практическая рекомендация для учебного Flask-проекта

    Для большинства проектов на уровне курса лучший путь:

  • начать с модульного монолита
  • покрыть критичные места тестами
  • только потом думать о микросервисах
  • Что такое модульный монолит в терминах Flask

    Типичная структура, к которой удобно постепенно прийти:

  • app/
  • - __init__.py с create_app() - api/ роуты и схемы ответов - services/ бизнес-логика - db/ сессии, модели, миграции
  • tests/
  • Ключевая идея: роуты становятся тонкими, а логика переезжает в services, которые проще unit-тестировать.

    Как тестирование связано с архитектурой

    Связь прямая:

  • чем больше логики спрятано в роуте, тем больше вам нужны интеграционные тесты
  • чем больше логики в чистых функциях и сервисах, тем больше вы можете покрыть unit-тестами
  • Практическое правило:

  • роут: парсинг запроса, вызов сервиса, формирование ответа
  • сервис: правила и операции, которые легко тестировать
  • слой БД: транзакции и запросы, которые проверяются интеграционно
  • AI-прототипирование: как просить генерировать тесты правильно

    AI хорошо генерирует тесты, если вы задаёте чёткий контракт.

    Шаблон промпта для теста эндпоинта:

    Дальше вы прогоняете тест и сверяете:

  • не перепутаны ли статусы
  • совпадает ли текст ошибок
  • нет ли зависимости от порядка выполнения тестов
  • Итог

    Теперь у вас есть цельная картина того, как довести Flask-прототип до более инженерного уровня:

  • pytest проверяет контракт и защищает от регрессий
  • Flask test client позволяет тестировать API без поднятия сервера
  • изолированная БД для тестов делает результаты воспроизводимыми
  • модульный монолит почти всегда лучший старт, а микросервисы стоит вводить только при ясных причинах
  • Этот набор навыков особенно важен, когда вы активно используете AI: скорость генерации кода растёт, но именно тесты удерживают качество и предсказуемость поведения приложения.