🏠 Домашнее задание 3: SQLAlchemy модели

📁 Из LMS: Python Advanced — Домашнее задание 3

⚡ Задание (из LMS)

  1. Создать движок для подключения к SQLite БД в памяти.
  2. Создать сессию для взаимодействия с БД через движок.
  3. Определить модель Product: id (Integer), name (String 100), price (Numeric с фиксированной точностью), in_stock (Boolean).
  4. Определить модель Category: id (Integer), name (String 100), description (String 255).
  5. Установить связь Product ↔ Category через category_id (ForeignKey).

Файл сдачи: models.py в папке проекта на ветке lesson/05-sqlalchemy-models.

Текст задания из LMS

Python Advanced: Домашнее задание 3

Источник: lms.itcareerhub.de — Python Advanced 5: Flask: SQLAlchemy

Задача 1: Создайте экземпляр движка для подключения к SQLite базе данных в памяти.

Задача 2: Создайте сессию для взаимодействия с базой данных, используя созданный движок.

Задача 3: Определите модель продукта Product со следующими типами колонок:

  • id: числовой идентификатор
  • name: строка (макс. 100 символов)
  • price: числовое значение с фиксированной точностью
  • in_stock: логическое значение

Задача 4: Определите связанную модель категории Category со следующими типами колонок:

  • id: числовой идентификатор
  • name: строка (макс. 100 символов)
  • description: строка (макс. 255 символов)

Задача 5: Установите связь между таблицами Product и Category с помощью колонки category_id.

Подготовка окружения

Шаг 0: Создать папку проекта

# В терминале (PowerShell или CMD):
mkdir python-advanced-practice
cd python-advanced-practice

Шаг 1: Инициализировать git-репозиторий

git init
git checkout -b lesson/05-sqlalchemy-models

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

# Создать venv в папке .venv
python -m venv .venv

# Активировать (Windows PowerShell):
.venv\Scripts\activate

# Активировать (Mac/Linux):
source .venv/bin/activate
💡 Как проверить, что venv активирован: в начале строки терминала появится (.venv).

Шаг 3: Установить зависимости

pip install sqlalchemy

# Зафиксировать зависимости
pip freeze > requirements.txt

Шаг 4: Создать .gitignore

# Создать файл .gitignore с содержимым:
.venv/
__pycache__/
*.pyc
*.db

Пошаговое решение

Создайте файл models.py в папке проекта. Ниже — полное решение с объяснением логики каждого шага.

Шаг 1: Импорты и создание движка (Задача 1)

Логика: движок — это точка подключения к БД. SQLite в памяти (:memory:) — самый простой вариант для учебных задач: не нужно создавать файл, данные исчезнут после завершения программы.

from sqlalchemy import create_engine, String, Boolean, ForeignKey
from sqlalchemy.types import Numeric
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session

# Задача 1: движок для SQLite в памяти
# "sqlite:///:memory:" — три двоеточия, затем :memory:
engine = create_engine("sqlite:///:memory:", echo=True)
# echo=True — выводить в консоль SQL-запросы (полезно для обучения)

Шаг 2: Базовый класс и объявление моделей

Логика: DeclarativeBase — основа системы ORM. Все модели наследуют от Base. SQLAlchemy отслеживает все подклассы Base и знает, какие таблицы нужно создать.

# Базовый класс — от него наследуют все модели
class Base(DeclarativeBase):
    pass

Шаг 3: Модель Category (Задача 4)

Логика: Category объявляем ПЕРВОЙ, потому что Product будет ссылаться на неё через ForeignKey. Хотя SQLAlchemy умеет разбираться с порядком, хорошая практика — объявлять «родительские» таблицы первыми.

# Задача 4: модель категории
class Category(Base):
    __tablename__ = "categories"       # имя таблицы в БД (обязательно)

    id: Mapped[int] = mapped_column(primary_key=True)
    # String(100) — VARCHAR(100) в SQL
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    # description может быть пустым — nullable=True (по умолчанию)
    description: Mapped[str | None] = mapped_column(String(255))

    # Задача 5: обратная сторона связи
    # Category.products — список всех продуктов этой категории
    products: Mapped[list["Product"]] = relationship(back_populates="category")

    def __repr__(self) -> str:
        return f"Category(id={self.id}, name={self.name!r})"

Шаг 4: Модель Product с ForeignKey (Задачи 3 и 5)

Логика: Numeric(10, 2) — тип для денег с фиксированной точностью: 10 цифр всего, 2 после запятой. Это точнее, чем Float (который иногда теряет точность). ForeignKey указывает на столбец в другой таблице.

# Задача 3: модель продукта
class Product(Base):
    __tablename__ = "products"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100), nullable=False)

    # Задача 3: числовое значение с фиксированной точностью
    # Numeric(10, 2): максимум 10 цифр, из них 2 после запятой
    # Например: 12345678.99
    price: Mapped[float] = mapped_column(Numeric(10, 2), default=0.00)

    # Задача 3: логическое значение (True/False)
    in_stock: Mapped[bool] = mapped_column(Boolean, default=True)

    # Задача 5: внешний ключ — ссылка на id в таблице categories
    # ВАЖНО: "categories.id" — имя ТАБЛИЦЫ (из __tablename__), не класса
    category_id: Mapped[int | None] = mapped_column(ForeignKey("categories.id"))

    # Задача 5: навигационная связь
    # Product.category — объект Category (или None, если нет категории)
    category: Mapped["Category | None"] = relationship(back_populates="products")

    def __repr__(self) -> str:
        return f"Product(id={self.id}, name={self.name!r}, price={self.price})"

Шаг 5: Создание таблиц и сессия (Задача 2)

Логика: create_all создаёт таблицы в БД по описанным моделям. Для SQLite в памяти это обязательно сделать ДО добавления данных. Сессия — это «единица работы»: набор операций, которые либо все выполняются, либо откатываются вместе.

# Создать все таблицы (categories и products)
Base.metadata.create_all(engine)

# Задача 2: создать сессию
# Рекомендуется использовать контекстный менеджер (with)
# чтобы сессия закрывалась автоматически
with Session(engine) as session:
    # Создаём категории
    electronics = Category(
        name="Электроника",
        description="Устройства и гаджеты"
    )
    food = Category(
        name="Продукты",
        description="Еда и напитки"
    )
    session.add_all([electronics, food])
    # flush() — отправляет SQL в БД, но без COMMIT
    # нужно, чтобы получить id категорий до создания продуктов
    session.flush()

    # Создаём продукты со ссылкой на категорию
    products = [
        Product(
            name="Ноутбук",
            price=55000.00,
            in_stock=True,
            category=electronics
        ),
        Product(
            name="Хлеб",
            price=65.50,
            in_stock=True,
            category=food
        ),
        Product(
            name="Наушники",
            price=8900.00,
            in_stock=False,
            category=electronics
        ),
    ]
    session.add_all(products)
    session.commit()   # фиксируем всё в БД

    print("Данные добавлены:")
    for p in products:
        cat_name = p.category.name if p.category else "—"
        print(f"  {p.name}: {p.price} руб., категория: {cat_name}, в наличии: {p.in_stock}")

Проверка в VS Code

Открыть проект

  1. Откройте VS Code.
  2. Меню File → Open Folder → выберите папку python-advanced-practice.
  3. VS Code предложит установить рекомендуемые расширения — нажмите Install All.

Выбрать интерпретатор

  1. Нажмите Ctrl+Shift+P → введите Python: Select Interpreter.
  2. Выберите интерпретатор из .venv (он должен быть помечен как «Recommended»).

Запустить файл

Вариант 1 — через терминал:

# В терминале VS Code (Ctrl+`)
python models.py

Вариант 2 — через F5 (отладчик).

Настройка launch.json для отладки (F5)

Создайте файл .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: models.py",
      "type": "debugpy",
      "request": "launch",
      "program": "${workspaceFolder}/models.py",
      "console": "integratedTerminal",
      "env": {
        "PYTHONPATH": "${workspaceFolder}"
      }
    }
  ]
}

После создания файла нажмите F5 — программа запустится в режиме отладки.

Точки останова (Breakpoints)

  1. Откройте models.py в VS Code.
  2. Кликните на номер строки слева от кода — появится красная точка (breakpoint).
  3. Нажмите F5 — выполнение остановится на этой строке.
  4. В панели «Variables» (левая панель) можно смотреть значения переменных.
  5. F10 — шаг вперёд (Step Over). F11 — войти в функцию (Step Into). F5 — продолжить до следующего breakpoint.

Консоль отладки (Debug Console)

При остановке на breakpoint откройте вкладку Debug Console (снизу). Там можно вводить Python-выражения и видеть значения:

# В Debug Console (когда программа остановлена):
electronics.id      # → 1
len(products)       # → 3
products[0].name    # → 'Ноутбук'

Типичные ошибки при выполнении ДЗ

Ошибка: ModuleNotFoundError: No module named 'sqlalchemy'

Причина: sqlalchemy не установлена или venv не активирован.

# Убедитесь, что venv активирован (должно быть (.venv) в строке)
.venv\Scripts\activate   # Windows
source .venv/bin/activate  # Mac/Linux

pip install sqlalchemy

Ошибка: OperationalError — no such table

Причина: забыли Base.metadata.create_all(engine) до сессии. Смотрите раздел ошибок.

Ошибка: ForeignKey не работает

Причина: в строке ForeignKey используется имя класса вместо имени таблицы. Правильно: ForeignKey("categories.id"), а не ForeignKey("Category.id").

Ошибка: price показывает много знаков после запятой

Причина: вы используете Float вместо Numeric(10, 2). Float — число с плавающей точкой (приблизительное), Numeric — точное десятичное число. Для денег всегда используйте Numeric.

# Неверно (может дать 65.4999999...)
price: Mapped[float] = mapped_column(Float)

# Верно (гарантированно 65.50)
from sqlalchemy.types import Numeric
price: Mapped[float] = mapped_column(Numeric(10, 2))
Подсказка: подробная теория по моделям — в разделе Теория. Примеры с ForeignKey — в разделе Примеры.

Как сдать задание

  1. Убедитесь, что models.py запускается без ошибок.
  2. Добавьте файлы в git и создайте коммит:
    git add models.py requirements.txt .gitignore
    git commit -m "lesson 05: SQLAlchemy models — Product, Category, ForeignKey"
  3. Загрузите на GitHub:
    git push origin lesson/05-sqlalchemy-models
  4. Скопируйте ссылку на репозиторий и вставьте в LMS как ответ на задание.