Transacciones¶
API de bloques transaccionales sync y async. Las firmas son código Python; la prosa es traducción manual.
atomic¶
Envuelve un bloque de código en una transacción de BD. Funciona tanto como context manager como decorador:
import dorm
# Context manager
with dorm.transaction.atomic():
...
# Decorador (sin paréntesis — `using` recibe la función)
@dorm.transaction.atomic
def update_balance(...):
...
# Decorador con alias
@dorm.transaction.atomic("replica_writer")
def report(...):
...
Semántica:
- En éxito, hace
COMMITal salir del bloque. - En excepción que sale del bloque, hace
ROLLBACK. - Las llamadas anidadas crean SAVEPOINTs, así que un fallo interno solo revierte el bloque interno (no el externo).
aatomic¶
Equivalente async de atomic. Mismo uso: como async with o como
decorador sobre funciones async.
from dorm.transaction import aatomic
# Context manager
async with aatomic():
a = await Author.objects.acreate(name="Alice", age=30)
await Book.objects.acreate(title="...", author=a)
# Decorador
@aatomic
async def transfer(...):
...
# Por alias
@aatomic("replica_writer")
async def replica_op(...):
...
Los bloques aatomic toman una conexión async del pool, así que la
puedes mantener a través de varios await sin bloquear el event loop.
on_commit¶
Programa un callable sin argumentos para ejecutarse después del commit de la transacción que lo envuelve. Patrón canónico para disparar efectos secundarios que no deben ocurrir si la transacción hace rollback (mandar email, encolar Celery/RQ, publicar a Kafka, llamar API externa).
from dorm import transaction
with transaction.atomic():
user = User.objects.create(name=name, email=email)
transaction.on_commit(lambda: send_welcome_email(user))
audit_log.record(user, action="signup")
# El email se manda solo si el commit triunfa.
Semántica:
- Fuera de un bloque
atomic(), el callback corre inmediatamente (paridad con Django). - Bloques
atomic()anidados difieren todos los callbacks al commit más externo. - Un rollback a cualquier profundidad descarta los callbacks programados ahí (y los merged de commits internos).
- Una excepción dentro del callback se loguea en el logger
dorm.transactionpero no se relanza — la BD ya commiteó.
aon_commit¶
def aon_commit(
callback: Callable[[], Any] | Callable[[], Awaitable[Any]],
using: str = "default",
) -> None
Contraparte async. Acepta tanto callables regulares como coroutine functions; las coroutines se await-ean al commit más externo.
async with transaction.aatomic():
user = await User.objects.acreate(name=name)
transaction.aon_commit(lambda: notify_kafka(user))
Fuera de un bloque aatomic(), las coroutines se programan con
asyncio.ensure_future para que el sitio de llamada no tenga que
hacer await.
Forzar rollback: set_rollback¶
El context manager devuelto por atomic() y aatomic() expone
set_rollback(True) para forzar un rollback al salir sin lanzar
excepción. Útil en fixtures de tests y patrones de "trabajo
especulativo":
with dorm.transaction.atomic() as tx:
Author.objects.create(name="especulativo", age=1)
if not is_useful(...):
tx.set_rollback(True)
# Bloque sale limpio; el rollback igual ocurre.
Cuando se llama a set_rollback(True), cualquier callback de
on_commit programado dentro del bloque se descarta — mismo
comportamiento que un rollback por excepción.
La variante async expone el mismo método en el context manager de
aatomic(), con semántica idéntica.
Avisos¶
- No mezcles sync y async sobre el mismo alias dentro de un mismo
request. El
atomic()sync y elaatomic()async pasan por pools distintos; una sentencia en uno es invisible para la transacción del otro. - Capturar excepciones dentro del bloque y esperar commit:
tragarse una excepción aún commitea —
atomic()solo hace rollback con excepciones que salen del bloque. - Mantener un
atomic()largo alrededor de I/O externa: deja los locks abiertos durante la parte lenta. Saca la I/O fuera del bloque cuando puedas. execute_script()cierra la transacción envolvente en SQLite: elexecutescript()de SQLite siempre emiteCOMMITantes y después del script. Es una limitación de SQLite, no un bug. PG no se ve afectado. Mira la guía de Transacciones para el detalle.
Para la versión auto-generada desde docstrings (en inglés), mira Transactions (English).