📖 Теория: SQLAlchemy — создание и наполнение БД

⚡ Кратко

SQLAlchemy — библиотека Python для работы с БД. Два подхода: ORM (объекты) и Core (SQL напрямую).

  • ORM: таблица → класс, строка → экземпляр, столбец → атрибут
  • Core: ниже уровнем, больше SQL-контроля и производительности
  • pip install SQLAlchemy — установка
  • Строка подключения: '<DBMS>+<library>://<user>:<password>@<host>:<port>/<database>'
  • SQLite в памяти: create_engine('sqlite:///:memory:')
  • Сессия: создать → add/изменить/удалить → commit → close
  • Логирование: logging.getLogger('sqlalchemy.engine')

Состояния объекта: Transient → Pending → Persistent → Detached/Deleted.

После close: объекты остаются в памяти, но отсоединяются от БД (Detached).

1. Введение в SQLAlchemy и ORM

SQLAlchemy — это мощная библиотека на языке Python, предназначенная для работы с базами данных с использованием SQL и программирования объектно-ориентированных запросов.

Она предоставляет полный набор инструментов для реализации моделей данных, выполнения запросов и анализа результатов в Python-приложениях.

SQLAlchemy предлагает два подхода к работе с базами данных: ORM (Object-Relational Mapping) и Core.

Полезно знать — SQLAlchemy: библиотека Python, предназначенная для работы с базами данных с использованием SQL и программирования объектно-ориентированных запросов.

2. ORM (Object-Relational Mapping)

ORM — это техника программирования, которая позволяет использовать объектно-ориентированный подход к работе с данными в базе данных. Она предоставляет возможность отображать записи базы данных на объекты в коде программы и наоборот.

Основные принципы ORM

  • Отображение объектов: ORM позволяет отображать таблицы базы данных на классы объектов в программе. Каждая строка таблицы становится экземпляром класса, а столбцы таблицы — атрибутами объекта.
  • Отображение связей: ORM позволяет отображать связи между таблицами базы данных на отношения между объектами (связь «один-к-одному», «один-ко-многим» или «многие-ко-многим»).
  • Абстрагирование от SQL: ORM позволяет работать с данными, не прибегая к написанию SQL-запросов. Разработчик использует объектно-ориентированный интерфейс для выполнения операций с данными.
  • Поддержка различных баз данных: ORM обеспечивает абстракцию над конкретной СУБД, что позволяет использовать один и тот же код с разными типами баз данных.

Преимущества ORM

  • Абстракция: работа с данными как с объектами Python, что упрощает разработку.
  • Лёгкость в использовании: более высокий уровень абстракции делает код более понятным.
  • Гибкость: возможность использовать наследование, миксины и другие возможности ООП.

Недостатки ORM

  • Производительность: может быть медленнее Core для сложных запросов из-за дополнительной абстракции.
  • Сложность: некоторые сложные SQL-запросы могут быть трудно реализовать через ORM.

3. Core

Core предоставляет более низкоуровневый доступ к базе данных, позволяя работать непосредственно с SQL-запросами и таблицами. Это даёт больше контроля и гибкости при работе с базой данных.

Преимущества Core

  • Производительность: может быть быстрее ORM для сложных запросов благодаря прямой работе с SQL.
  • Гибкость: позволяет точно контролировать выполнение запросов и структуру данных.

Недостатки Core

  • Сложность: требует глубоких знаний SQL и может сделать код менее читаемым.
  • Время разработки: разработка может занять больше времени из-за необходимости писать более сложный код.

4. Выбор подхода

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

  • Если проект включает в себя сложную логику работы с данными и требует высокой производительности, Core может быть предпочтительнее.
  • Для простых приложений или приложений, где скорость разработки является ключевым фактором, ORM может быть более подходящим выбором.
Контрольный вопрос из лекции: какой из подходов в SQLAlchemy предоставляет более низкоуровневый доступ к базе данных? Ответ: Core.

5. Установка SQLAlchemy

Для начала работы с SQLAlchemy необходимо установить саму библиотеку с помощью pip:

pip install SQLAlchemy

6. Конфигурация подключения

Так же как и при подключении к СУБД через интерфейс СУБД (например, Workbench), необходимо указать параметры подключения. Шаблон строки подключения:

'<DBMS>+<library>://<user>:<password>@<host>:<port>/<database>'
  • <DBMS>+<library> — к какой СУБД будет подключение и через какую библиотеку
  • <user> — имя пользователя
  • <password> — пароль для указанного пользователя
  • <host> — адрес сервера, к которому вы хотите подключиться
  • <port> — номер порта, на котором работает СУБД (например, для MySQL стандартный порт: 3306)
  • <database> — название конкретной базы данных, к которой вы хотите получить доступ

Пример строки подключения к MySQL:

'mysql+pymysql://user:password@localhost:3306/mydatabase'

7. Создание движка (create_engine)

Для создания движка используется функция create_engine(), которая требует настройки через строку подключения и может принимать дополнительные параметры, настраивающие его поведение. Движок SQLAlchemy служит фундаментом для взаимодействия с базой данных, управляя подключениями, сессиями, транзакциями и выполнением SQL-запросов.

Примеры создания движка с разными СУБД:

from sqlalchemy import create_engine

# PostgreSQL
engine = create_engine('postgresql://user:password@localhost:5432/mydatabase')

# MySQL (требует pip install pymysql)
engine = create_engine('mysql+pymysql://user:password@localhost:3306/mydatabase')

# SQLite (для локального файла)
engine = create_engine('sqlite:///path/to/database.db')

# SQLite (в оперативной памяти — данные не сохраняются)
engine = create_engine('sqlite:///:memory:')
Важно: для подключения к конкретной СУБД необходимо установить библиотеку-драйвер. Например, для MySQL:
pip install pymysql

8. Сессия SQLAlchemy

Полезно знать — Сессия в SQLAlchemy: это рабочий интерфейс между объектами в вашем приложении и базой данных. Она управляет всем, начиная от открытия соединения с базой данных до завершения транзакции. Сессия отслеживает объекты, которые вы загружаете или ассоциируете с ней, и автоматически записывает изменения в базу данных.

Жизненный цикл сессии

  1. Открытие сессии: сессия начинается с создания экземпляра Session.
  2. Работа с объектами: в сессию могут быть добавлены новые объекты, существующие объекты могут быть изменены или удалены.
  3. Фиксация изменений: изменения фиксируются в базе данных с помощью метода commit().
  4. Закрытие сессии: сессия может быть закрыта методом close(). После закрытия сессии объекты остаются в памяти, но отсоединяются от базы данных.

Состояния объекта в сессии

  • Transient: объект создан, но не связан с сессией.
  • Pending: объект связан с сессией, но ещё не сохранён в базу данных.
  • Persistent: объект сохранён в базу данных и находится под управлением сессии.
  • Detached: объект был связан с сессией, но сейчас отсоединён от неё.
  • Deleted: объект был удалён из базы данных в рамках текущей сессии.
Контрольный вопрос из лекции: что происходит с объектами после закрытия сессии в SQLAlchemy? Ответ: объекты остаются в памяти, но отсоединяются от базы данных (переходят в состояние Detached).

Создание сессии (стиль лекции — 1.x)

В лекции сессия создаётся через sessionmaker — фабрику сессий:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# Создаём экземпляр движка SQLAlchemy
engine = create_engine('sqlite:///my_database.db')

# Создаём класс Session, который будет использоваться для взаимодействия с БД
Session = sessionmaker(bind=engine)

# Создаём экземпляр сессии
session = Session()
Современный подход (SQLAlchemy 2.x): рекомендуется использовать контекстный менеджер, чтобы сессия закрывалась автоматически:
from sqlalchemy.orm import Session

with Session(engine) as session:
    # работа с объектами
    session.commit()
Подробнее об отличиях 1.x от 2.x — в разделе «Старый vs Новый».

Полный пример использования сессии

Базовое использование сессии для добавления объектов (пример из лекции, стиль 1.x):

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base

Base = declarative_base()
engine = create_engine('sqlite:///example.db')

# Определяем класс User, который наследуется от базового класса Base.
# Этот класс представляет собой сущность базы данных.
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(30))
    age = Column(Integer)

Session = sessionmaker(bind=engine)
session = Session()

# Для того чтобы в базе данных появилась таблица, вызываем метод create_all()
# объекта metadata базового класса Base.
# SQLAlchemy автоматически анализирует классы моделей данных и создаёт
# соответствующие таблицы в базе данных.
Base.metadata.create_all(engine)

# Создание нового пользователя
new_user = User(name="John Doe", age=30)
# Добавляем объект в сессию с помощью метода add().
session.add(new_user)
# Вызываем метод commit(), чтобы зафиксировать все изменения в базе данных.
session.commit()
Это код из лекции (SQLAlchemy 1.x). В современных проектах используют DeclarativeBase, Mapped и mapped_column. Сравнение стилей — в разделе «Старый vs Новый».

9. Логирование событий

Полезно знать — Логирование: это процесс записи информации о происходящих операциях в системе.

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

SQLAlchemy позволяет настраивать логирование через стандартный модуль logging в Python:

import logging

# Настройка базового логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('sqlalchemy.engine')

# Включение логирования SQL-запросов
logger.setLevel(logging.INFO)

Теперь SQLAlchemy будет выводить информацию о всех SQL-запросах и ответах. Эта конфигурация позволяет глубоко интегрироваться с процессом работы с базой данных, делая его максимально прозрачным и контролируемым для разработчика.

10. Практическая работа (задачи лекции)

В лекции даны две практические задачи:

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

Полные решения — в разделе «Задания» и «Решения».

⚠️ За рамками лекции — для общего кругозора
Следующий раздел не является частью учебной лекции. Он добавлен для понимания полной картины инструментария SQLAlchemy. Код-примеры взяты из официального API SQLAlchemy 2.x.

Стили маппинга в SQLAlchemy 2.x

В лекции из всех вариантов ORM изучается только декларативный стиль (Declarative ORM). SQLAlchemy 2.x поддерживает пять стилей работы с моделями — от полного отказа от ORM до dataclass-удобства.

1. Core Table — без ORM-классов

Таблицы описываются напрямую через Table и MetaData. Никаких Python-классов — только структура таблиц и SQL-запросы. Доступ к столбцам через users.c.id.

from sqlalchemy import Table, Column, Integer, String, MetaData

metadata = MetaData()
users = Table(
    "users", metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(30)),
)

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

2. Declarative ORM — основной современный вариант ← изучается в лекции

Класс является одновременно Python-классом, ORM-моделью и описанием таблицы. Основной стиль SQLAlchemy 2.x. Лучший выбор для Flask и FastAPI-приложений.

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))

Когда использовать: большинство современных проектов, Flask, FastAPI, любые новые приложения.

3. Imperative / Classical Mapping — класс и таблица отдельно

Обычный Python-класс (без наследования) связывается с таблицей через registry().map_imperatively(). Позволяет полностью отделить доменные классы от описания БД.

from sqlalchemy.orm import registry

mapper_registry = registry()

class User:
    pass  # обычный Python-класс

users_table = Table("users", mapper_registry.metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(30)),
)

mapper_registry.map_imperatively(User, users_table)

Когда использовать: Domain-Driven Design (DDD), когда домен не должен «знать» о базе данных.

4. Hybrid Declarative — декларативный класс, внешняя таблица

Класс декларативный, но таблица описана отдельно и подключается через __table__.

class User(Base):
    __table__ = users_table  # таблица описана отдельно

Когда использовать: когда таблицы уже описаны через Core Table, но нужны ORM-возможности.

5. Dataclass Mapping — dataclass + ORM

Сочетает удобство dataclass (автогенерация __init__, __repr__) с возможностями ORM.

from sqlalchemy.orm import MappedAsDataclass, DeclarativeBase

class Base(MappedAsDataclass, DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    name: Mapped[str]

Когда использовать: когда нужен dataclass-стиль инициализации объектов.

Сравнительная таблица стилей

Стиль Когда использовать
Declarative ORM ← учим в лекции Основной выбор для большинства проектов (Flask, FastAPI)
Core Table SQL-контроль без ORM-объектов; скрипты, миграции
Imperative / Classical DDD; доменные классы не должны зависеть от БД
Hybrid Declarative Таблицы уже описаны через Core, нужен ORM поверх
Dataclass Mapping Dataclass-стиль инициализации + ORM
Вывод: в этом курсе изучается Declarative ORM — основной и наиболее распространённый стиль SQLAlchemy 2.x. Остальные стили — продвинутые варианты для специализированных задач.