CRUD: создание, чтение, обновление, удаление
CRUD — четыре базовые операции с данными: Create (создать), Read (прочитать), Update (изменить), Delete (удалить). На них держится почти любое приложение.
Все операции идут через db.session — «рабочий стол» изменений. Добавил/изменил/удалил объекты — и зафиксировал всё одним commit. В современном SQLAlchemy чтение делают через select(), а старый Model.query считается легаси.
Сессия — это единица работы: ты набрасываешь в неё изменения, а commit применяет их к базе одной транзакцией. Если что-то пошло не так — rollback отменяет всё разом. Разберём каждую операцию.
Create — создать объект, добавить в сессию, зафиксировать:
user = User(name="Анна", email="[email protected]")
db.session.add(user)
db.session.commit()
Read — современный способ через select и db.session.execute:
from sqlalchemy import select
# все пользователи
users = db.session.execute(select(User)).scalars().all()
# один по условию
u = db.session.execute(
select(User).filter_by(email="[email protected]")
).scalar_one_or_none()
# по id с авто-404
user = db.get_or_404(User, 1)
Update — просто меняешь атрибут и коммитишь, ORM сам сформирует UPDATE:
user.name = "Анна Петрова"
db.session.commit()
Delete — удаляешь объект из сессии и коммитишь:
db.session.delete(user)
db.session.commit()
Ключ к пониманию SQLAlchemy — концепция сессии как единицы работы. Сессия — это не соединение с базой, а «рабочий стол», на котором копятся изменения: добавленные, изменённые и удалённые объекты. Пока ты не вызвал commit, ничего в базу не ушло — всё живёт в памяти сессии. commit применяет накопленное одной транзакцией по принципу «всё или ничего», а при ошибке rollback откатывает разом. Именно поэтому самая частая ошибка новичка — забытый commit: объект добавлен, но не сохранён. Второй сдвиг — отказ от легаси Model.query в пользу select() с db.session.execute: это единый современный интерфейс чтения, который к тому же одинаков в чистом SQLAlchemy и во Flask-SQLAlchemy.
Как работает под капотом
Сессия отслеживает состояние объектов: новые (add), изменённые (грязные), удалённые. При commit она вычисляет, какой SQL нужен, и выполняет всё в одной транзакции. До commit изменения живут только в памяти сессии.
db.session (рабочий стол)
┌────────────────────────────┐
│ add(user) → INSERT │
│ user.name=.. → UPDATE │
│ delete(old) → DELETE │
└────────────────────────────┘
│ commit()
▼
одна транзакция в БД: всё или ничего
│ ошибка? → rollback (откат всего)
Смоделируем сессию как «рабочий стол» с отложенным commit.
class Session:
def __init__(self):
self.db = {}
self.pending = []
def add(self, key, value):
self.pending.append(("add", key, value))
def delete(self, key):
self.pending.append(("del", key, None))
def commit(self):
for op, key, value in self.pending:
if op == "add": self.db[key] = value
elif op == "del": self.db.pop(key, None)
self.pending.clear()
s = Session()
s.add("u1", {"name": "Анна"})
s.add("u2", {"name": "Боб"})
s.commit()
s.delete("u1")
print("до commit:", "u1" in s.db) # ещё True
s.commit()
print("после commit:", "u1" in s.db, "| осталось:", s.db)
Запусти: изменения копятся и применяются только на commit — точно как в SQLAlchemy. Это объясняет, почему без commit данные «не сохраняются».
Частые ошибки
- Забыть commit. Самая частая ошибка: add без commit ничего не сохранит.
- Использовать легаси Model.query. В новых проектах предпочитай select() + db.session.execute.
- Ловить отсутствие записи вручную. Для «найти или 404» есть db.get_or_404 — короче и правильнее.
Best practices
- Группируй связанные изменения и коммить одной транзакцией.
- При ошибке делай db.session.rollback(), не оставляй сессию в подвешенном состоянии.
- Читай через select(...).scalars(); для поиска по id — get_or_404.
Что запомнить
- CRUD идёт через db.session: add, изменение атрибута, delete.
- Изменения применяются транзакцией только на commit; иначе теряются.
- Чтение — через select() + db.session.execute().scalars(); по id — get_or_404.
- При ошибке делают rollback, не оставляя сессию подвешенной.
Итог: CRUD идёт через db.session — add/commit/delete и чтение через select(). Изменения применяются транзакцией на commit. Дальше — связи между таблицами.