🐛 Типичные ошибки при работе с запросами
⚡ Топ-3 ошибки
- Забыть commit() — изменения не сохраняются в БД.
- NoResultFound от .one() — нет записи, а код ожидает ровно одну.
- Использовать session.query() в 2.x — устаревший API, в новых проектах использовать select() + session.scalars().
Ошибка 1: Забыли вызвать commit()
Неверно
with Session(engine) as session:
session.add(User(name="Alice", age=30))
# commit() не вызван — данные не сохранятся!
Верно
with Session(engine) as session:
session.add(User(name="Alice", age=30))
session.commit() # обязательно!
Почему: без commit() изменения существуют только в рамках транзакции в памяти. При закрытии with-блока транзакция автоматически откатывается.
Ошибка 2: NoResultFound при .one()
Опасный код
with Session(engine) as session:
# Если нет пользователя с id=999 — будет исключение!
stmt = select(User).where(User.id == 999)
user = session.scalars(stmt).one() # raises NoResultFound
Безопасный вариант
with Session(engine) as session:
# .one_or_none() — безопаснее, если результат может отсутствовать
stmt = select(User).where(User.id == 999)
user = session.scalars(stmt).one_or_none()
if user:
print(user.name)
else:
print("Пользователь не найден")
Когда использовать .one(): только когда вы уверены, что запись существует и единственная (например, lookup по уникальному полю в системе с гарантиями).
Ошибка 3: Нет проверки после .first()
Неверно
with Session(engine) as session:
user = session.scalars(select(User)).first()
print(user.name) # AttributeError: 'NoneType' has no attribute 'name'
# если пользователей нет!
Верно
with Session(engine) as session:
user = session.scalars(select(User)).first()
if user:
print(user.name)
else:
print("Список пуст")
Ошибка 4: Изменение объекта без сохранения
Неверно
with Session(engine) as session:
user = session.get(User, 1)
user.age = 35
# commit() забыли — изменение не сохранится!
Верно
with Session(engine) as session:
user = session.get(User, 1)
if user:
user.age = 35
session.commit() # только после этого UPDATE дойдёт до БД
Ошибка 5: Двойное равенство в Python vs SQL
Неверно
# Питон is/== — для Python-объектов
# в фильтре нужно использовать == (SQLAlchemy перегружает оператор)
stmt = select(User).where(User.name is "Alice") # НЕВЕРНО — Python is, не SQL
Верно
# == в SQLAlchemy генерирует SQL WHERE name = 'Alice'
stmt = select(User).where(User.name == "Alice") # верно
# Для проверки NULL — специальный метод
stmt = select(User).where(User.age.is_(None)) # WHERE age IS NULL
Ошибка 6: Использование session.query() в 2.x
Устаревший стиль (из лекции)
# session.query() — Legacy Query API, устарел в 2.x
users = session.query(User).filter(User.age > 25).all()
user = session.query(User).get(1) # .get() на Query — устарел
Современный стиль (2.x)
from sqlalchemy import select
# select() + session.scalars() — рекомендуемый подход
stmt = select(User).where(User.age > 25)
users = session.scalars(stmt).all()
# session.get() на Session — современный способ по ключу
user = session.get(User, 1)
Примечание: session.query() продолжает работать в SQLAlchemy 2.x как legacy. Но в IDE появляется предупреждение об устаревании, а в будущей версии поддержка может быть удалена.
Ошибка 7: in_() с одиночным значением
Неверно
# in_() ожидает список/кортеж, не строку
stmt = select(User).where(User.name.in_("Alice")) # итерирует по символам!
Верно
# Передавайте список или кортеж
stmt = select(User).where(User.name.in_(["Alice"])) # список из одного элемента
# или используйте ==
stmt = select(User).where(User.name == "Alice")
Ошибка 8: like() — забыли % wildcard
Неверно
# Без % — точное совпадение, не поиск подстроки
stmt = select(User).where(User.name.like("Al")) # найдёт только "Al", не "Alice"
Верно
# % — любое количество символов
stmt = select(User).where(User.name.like("Al%")) # "Alice", "Alena", "Al" — все
stmt = select(User).where(User.name.like("%ice")) # "Alice", "Clarice" — окончание
stmt = select(User).where(User.name.like("%li%")) # содержит "li"