Метод with_for_update
Метод with_for_update класса Select добавляет
к SQL-запросу конструкцию FOR UPDATE, которая блокирует
выбранные строки до завершения транзакции. Это нужно, чтобы
другие транзакции не могли изменить эти строки, пока текущая
транзакция не завершится. Первым необязательным параметром
метод принимает nowait, вторым - read,
третьим - of, четвертым - skip_locked,
пятым - key_share. Метод возвращает новый объект
Select с добавленной блокировкой.
Синтаксис
Select.with_for_update(nowait=False, read=False, of=None, skip_locked=False, key_share=False)
Пример
Давайте создадим простой запрос с блокировкой строк таблицы
articles и посмотрим итоговый SQL:
from sqlalchemy import select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
text: Mapped[str]
status: Mapped[str]
num: Mapped[int]
stmt = select(Article).with_for_update()
print(stmt)
Результат выполнения кода:
"SELECT articles.id, articles.title, articles.text, articles.status, articles.num FROM articles FOR UPDATE"
Пример
Давайте передадим параметр nowait, чтобы запрос
не ждал освобождения блокировки, а сразу падал с ошибкой:
from sqlalchemy import select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
text: Mapped[str]
status: Mapped[str]
num: Mapped[int]
stmt = select(Article).where(Article.status == 'active').with_for_update(nowait=True)
print(stmt)
Итоговый SQL запрос:
SELECT articles.id, articles.title, articles.text, articles.status, articles.num
FROM articles
WHERE articles.status = 'active'
FOR UPDATE NOWAIT
Пример
Давайте используем параметр skip_locked, чтобы
пропустить уже заблокированные строки вместо ожидания:
from sqlalchemy import select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
text: Mapped[str]
status: Mapped[str]
num: Mapped[int]
stmt = select(Article).with_for_update(skip_locked=True)
print(stmt)
Итоговый SQL запрос:
SELECT articles.id, articles.title, articles.text, articles.status, articles.num
FROM articles
FOR UPDATE SKIP LOCKED
Пример
Давайте выполним запрос с блокировкой в реальной транзакции и выведем полученные строки:
from sqlalchemy import create_engine, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
text: Mapped[str]
status: Mapped[str]
num: Mapped[int]
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Article(title='article 1', text='text 1', status='active', num=1))
session.add(Article(title='article 2', text='text 2', status='active', num=2))
session.commit()
stmt = select(Article).where(Article.status == 'active').with_for_update()
res = session.execute(stmt).scalars().all()
for article in res:
print(article.title)
Результат выполнения кода:
"article 1"
"article 2"
Смотрите также
-
класс
Select,
который представляет SQL-запрос SELECT -
метод
where,
который добавляет условие фильтрации к запросу -
метод
execution_options,
который задает опции выполнения запроса -
метод
cte,
который превращает запрос в общее табличное выражение