🐛 Типичные ошибки: SQLAlchemy — движок, сессия, состояния

⚡ Топ-5 ошибок

  1. Нет create_allOperationalError: no such table. Решение: Base.metadata.create_all(engine) до открытия сессии.
  2. Нет commit — данные добавляются в сессию, но не сохраняются в БД. Всегда вызывайте session.commit().
  3. Перепутаны состояния объекта — DetachedInstanceError после session.close(): объект в памяти, но отсоединён от БД.
  4. Неверная строка подключения — нет нужной библиотеки-драйвера (например, pymysql для MySQL).
  5. Сессия не закрыта — утечка соединений. Используйте контекстный менеджер with Session(engine) as session:.

Ошибка 1: Забыли create_all — таблица не создана

Симптом: OperationalError: (sqlite3.OperationalError) no such table: users
# Неверно — пропущен create_all
class User(Base):
    __tablename__ = 'users'
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    age: Mapped[int] = mapped_column(Integer)

# Base.metadata.create_all(engine)  ← пропущено!

with Session(engine) as session:
    session.add(User(name='John Doe', age=30))
    session.commit()  # OperationalError: no such table: users
# Верно — сначала создаём таблицы, потом открываем сессию
Base.metadata.create_all(engine)  # ОБЯЗАТЕЛЬНО!

with Session(engine) as session:
    session.add(User(name='John Doe', age=30))
    session.commit()  # теперь работает

Ошибка 2: Нет commit — данные не сохраняются

# Неверно — забыли commit
with Session(engine) as session:
    user = User(name='Alice', age=25)
    session.add(user)
    # session.commit() ← пропущено!
# При выходе из with-блока сессия выполняет rollback
# Данные НЕ сохранились в БД!
# Верно
with Session(engine) as session:
    user = User(name='Alice', age=25)
    session.add(user)
    session.commit()  # ОБЯЗАТЕЛЬНО для сохранения
    print(f'Сохранён пользователь id={user.id}')

Ошибка 3: DetachedInstanceError — обращение к объекту вне сессии

Симптом: DetachedInstanceError: Instance <User at 0x...> is not bound to a Session

После закрытия сессии объекты переходят в состояние Detached — они остаются в памяти, но отсоединены от БД. Обращение к атрибутам, которые не были загружены, вызывает ошибку.

# Неверно — обращение к объекту после закрытия сессии
with Session(engine) as session:
    user = session.get(User, 1)
# Сессия закрыта — user в состоянии Detached

print(user.name)  # это работает (значение уже загружено)
# Но если есть lazy-загружаемые связи:
# print(user.orders)  # DetachedInstanceError!
# Верно — получить все нужные данные внутри сессии
with Session(engine) as session:
    user = session.get(User, 1)
    user_name = user.name  # сохраняем значение внутри сессии

print(user_name)  # OK — это строка, не ORM-объект
Контрольный вопрос из лекции: что происходит с объектами после session.close()? Объекты остаются в памяти, но отсоединяются от базы данных (состояние Detached).

Ошибка 4: Неверная строка подключения / нет драйвера

Симптом: ModuleNotFoundError: No module named 'pymysql' или NoSuchModuleError
# Неверно — используется MySQL, но pymysql не установлен
engine = create_engine('mysql+pymysql://user:password@localhost:3306/mydb')
# ModuleNotFoundError: No module named 'pymysql'
# Верно — сначала установить драйвер
# pip install pymysql
engine = create_engine('mysql+pymysql://user:password@localhost:3306/mydb')

Для SQLite драйвер не нужен — он встроен в Python:

# SQLite не требует дополнительных библиотек
engine = create_engine('sqlite:///:memory:')  # всегда работает

Ошибка 5: Сессия не закрыта — утечка соединений

# Неверно — ручное управление без гарантии закрытия
session = Session(engine)
user = User(name='Bob', age=30)
session.add(user)
session.commit()
# Если здесь возникнет исключение — session.close() не вызовется!
session.close()  # может не выполниться
# Верно — контекстный менеджер гарантирует закрытие
with Session(engine) as session:
    user = User(name='Bob', age=30)
    session.add(user)
    session.commit()
# session.close() вызывается автоматически при любом выходе из блока