SQLAlchemy ORM: объектно-реляционное отображение в Python

Мы изучили SQLAlchemy Core — мощный способ работы с SQL через Python-код. Теперь познакомимся с SQLAlchemy ORM — еще более удобным подходом, где таблицы базы данных становятся Python-классами, а записи — объектами.

Что такое ORM?

ORM (Object-Relational Mapping) — это технология, которая связывает объекты в коде с записями в базе данных. Вместо написания SQL-запросов, вы работаете с обычными Python-объектами.

Основная идея

Без ORM (SQLAlchemy Core):

Python 3.13
# Создаем SQL-запрос для получения пользователя
select_query = select(users_table).where(users_table.c.id == 1)
result = connection.execute(select_query)
user_row = result.fetchone()
print(user_row.name)  # Работаем с Row объектом

С ORM:

Python 3.13
# Работаем с Python-объектом напрямую
user = session.get(User, 1)  # User - это Python класс
print(user.name)  # Обращаемся к атрибуту объекта
user.email = "new@email.com"  # Изменяем как обычный объект
session.commit()  # Сохраняем изменения

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

  • 🐍 Pythonic код — работаете с объектами, а не со строками SQL
  • 🔄 Автоматическое отслеживание изменений — ORM сам знает что нужно сохранить
  • 🔗 Простые связи между таблицамиuser.posts вместо JOIN запросов
  • ✅ Валидация данных — проверки на уровне Python-классов

Почему сначала Core, потом ORM?

Понимание основ — Core показал как работают SQL-запросы под капотом
Контроль — иногда нужен точный контроль над SQL
Отладка — зная Core, легче понять что делает ORM
Выбор инструмента — для каждой задачи подходящий уровень абстракции

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

ORM входит в основной пакет SQLAlchemy:

Python 3.13
pip install sqlalchemy

Создание моделей

В ORM таблицы описываются как Python-классы. Начнем с простого примера:

Шаг 1: Базовая настройка

Python 3.13
>>> from sqlalchemy import create_engine
>>> from sqlalchemy.ext.declarative import declarative_base
>>> from sqlalchemy.orm import sessionmaker

# Базовый класс для всех моделей
>>> Base = declarative_base()

# Подключение к базе данных
>>> engine = create_engine('sqlite:///orm_tasks.db')

>>> print("✅ Базовая настройка готова!")

✅ Базовая настройка готова!

Шаг 2: Создание модели

Python 3.13
>>> from sqlalchemy import Column, Integer, String, Boolean

# Модель = Python-класс = таблица в БД
>>> class Task(Base):
...     __tablename__ = 'tasks'  # Имя таблицы

>>>     # Поля таблицы как атрибуты класса
...     id = Column(Integer, primary_key=True)
...     title = Column(String, nullable=False)
...     completed = Column(Boolean, default=False)

>>>     # Красивое отображение объекта
...     def __repr__(self):
...         status = "✅" if self.completed else "⏳"
...         return f"<Task: {status} {self.title}>"

>>> print("✅ Модель Task создана!")

✅ Модель Task создана!

Шаг 3: Создание таблицы и сессии

Python 3.13
# Создаем таблицу в базе данных
>>> Base.metadata.create_all(engine)

# Создаем сессию для работы с данными
>>> Session = sessionmaker(bind=engine)
>>> session = Session()

>>> print("✅ Таблица создана, сессия готова!")

✅ Таблица создана, сессия готова!

CRUD операции через ORM

Теперь изучим основные операции с данными. Каждая операция в отдельном примере:

CREATE: Создание объектов

Python 3.13
# Создаем объекты как обычные Python-экземпляры
>>> task1 = Task(title="Изучить ORM")
>>> task2 = Task(title="Написать код")

# Добавляем в сессию и сохраняем
>>> session.add_all([task1, task2])
>>> session.commit()

>>> print("✅ CREATE: Созданы задачи:")
>>> print(f"  {task1}")
>>> print(f"  {task2}")

✅ CREATE: Созданы задачи:
<Task: ⏳ Изучить ORM>
<Task: ⏳ Написать код>

READ: Чтение данных

Python 3.13
# Получить все задачи
>>> all_tasks = session.query(Task).all()
>>> print("📋 READ: Все задачи:")
>>> for task in all_tasks:
...     print(f"  {task}")

📋 READ: Все задачи:
<Task: ⏳ Изучить ORM>
<Task: ⏳ Написать код>
Python 3.13
# Получить конкретную задачу по ID
>>> task = session.get(Task, 1)
>>> print(f"🎯 Задача с ID=1: {task}")

🎯 Задача с ID=1: <Task: ⏳ Изучить ORM>

UPDATE: Обновление объектов

Python 3.13
# Просто меняем атрибут объекта!
>>> task = session.get(Task, 1)
>>> task.completed = True
>>> task.title = "Изучить ORM ✨"

# Сохраняем изменения
>>> session.commit()

>>> print(f"🔄 UPDATE: {task}")

🔄 UPDATE: <Task: ✅ Изучить ORM ✨>

DELETE: Удаление объектов

Python 3.13
# Находим и удаляем задачу
>>> task_to_delete = session.get(Task, 2)
>>> session.delete(task_to_delete)
>>> session.commit()

>>> print(f"🗑️ DELETE: Удалена задача")

🗑️ DELETE: Удалена задача
# Проверяем что осталось >>> remaining_tasks = session.query(Task).all() >>> print(f"📋 Осталось задач: {len(remaining_tasks)}") >>> for task in remaining_tasks: ... print(f" {task}")
📋 Осталось задач: 1
<Task: ✅ Изучить ORM ✨>

Главные преимущества ORM

  • Простота — работаете с объектами как обычно в Python
  • Автоматизация — ORM сам отслеживает изменения
  • Безопасность — защита от SQL-инъекций из коробки
  • Читаемость — код понятен без знания SQL

Отношения между таблицами

ORM позволяет легко связывать таблицы. Покажем на простом примере:

Python 3.13
>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy.orm import relationship

# Пользователь (один) → Задачи (много)
>>> class User(Base):
...     __tablename__ = 'users'

>>>     id = Column(Integer, primary_key=True)
...     name = Column(String, nullable=False)

>>>     # Связь с задачами
...     tasks = relationship("UserTask")

# Задача принадлежит пользователю
>>> class UserTask(Base):
...     __tablename__ = 'user_tasks'

>>>     id = Column(Integer, primary_key=True)
...     title = Column(String, nullable=False)
...     user_id = Column(Integer, ForeignKey('users.id'))

# Создаем таблицы и данные
>>> Base.metadata.create_all(engine)
>>> session = Session()

>>> user = User(name="Анна")
>>> task1 = UserTask(title="Изучить Python", user_id=1)
>>> task2 = UserTask(title="Написать код", user_id=1)

>>> session.add_all([user, task1, task2])
>>> session.commit()

# Магия ORM: получаем связанные объекты
>>> print(f"👤 Пользователь: {user.name}")
>>> print(f"📋 Задач: {len(user.tasks)}")
>>> for task in user.tasks:
...     print(f"  - {task.title}")

>>> session.close()

👤 Пользователь: Анна
📋 Задач: 2
- Изучить Python
- Написать код

Главное преимущество: user.tasks автоматически получает связанные записи без написания JOIN запросов!

Сравнение подходов

АспектЧистый SQLSQLAlchemy CoreSQLAlchemy ORM
СинтаксисSQL строкиPython-функцииPython-объекты
БезопасностьРучнаяАвтоматическаяАвтоматическая
Связи таблицJOIN запросыСложные выраженияuser.tasks
ИзмененияUPDATE SQLupdate().values()obj.field = value
Кривая обученияНужно знать SQLСредняяПростая для начала
ПроизводительностьМаксимальнаяВысокаяХорошая

Что мы изучили? ✅

Теперь вы знаете основы SQLAlchemy ORM:

🎯 Концепция ORM — объекты вместо SQL
🎯 Создание моделей — классы как таблицы
🎯 CRUD через объекты — интуитивные операции
🎯 Связи между таблицами — простая работа с отношениями

Главное преимущество ORM перед Core?