Una gu铆a completa para la gesti贸n de sesiones de SQLAlchemy en Python, enfocada en t茅cnicas robustas de manejo de transacciones para garantizar la integridad y consistencia de los datos.
Gesti贸n de Sesiones de SQLAlchemy en Python: Dominando el Manejo de Transacciones para la Integridad de los Datos
SQLAlchemy es una biblioteca de Python poderosa y flexible que proporciona un conjunto de herramientas completo para interactuar con bases de datos. En el coraz贸n de SQLAlchemy se encuentra el concepto de sesi贸n, que act煤a como una zona de preparaci贸n para todas las operaciones que realiza en su base de datos. La gesti贸n adecuada de sesiones y transacciones es crucial para mantener la integridad de los datos y garantizar un comportamiento consistente de la base de datos, especialmente en aplicaciones complejas que manejan solicitudes concurrentes.
Comprendiendo las Sesiones de SQLAlchemy
Una Sesi贸n de SQLAlchemy representa una unidad de trabajo, una conversaci贸n con la base de datos. Realiza un seguimiento de los cambios realizados en los objetos, lo que le permite persistirlos en la base de datos como una 煤nica operaci贸n at贸mica. Piense en ello como un espacio de trabajo donde realiza modificaciones en los datos antes de guardarlos oficialmente. Sin una sesi贸n bien gestionada, corre el riesgo de inconsistencias en los datos y una posible corrupci贸n.
Creaci贸n de una Sesi贸n
Antes de poder comenzar a interactuar con su base de datos, necesita crear una sesi贸n. Esto implica primero establecer una conexi贸n con la base de datos utilizando el motor de SQLAlchemy.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# Cadena de conexi贸n a la base de datos
db_url = 'sqlite:///:memory:' # Reemplace con la URL de su base de datos (por ejemplo, PostgreSQL, MySQL)
# Crear un motor
engine = create_engine(db_url, echo=False) # echo=True para ver el SQL generado
# Definir una base para modelos declarativos
Base = declarative_base()
# Definir un modelo simple
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
# Crear la tabla en la base de datos
Base.metadata.create_all(engine)
# Crear una clase de sesi贸n
Session = sessionmaker(bind=engine)
# Instanciar una sesi贸n
session = Session()
En este ejemplo:
- Importamos los m贸dulos de SQLAlchemy necesarios.
- Definimos una cadena de conexi贸n a la base de datos (`db_url`). Este ejemplo utiliza una base de datos SQLite en memoria para simplificar, pero deber铆a reemplazarla con una cadena de conexi贸n adecuada para su sistema de base de datos (por ejemplo, PostgreSQL, MySQL). El formato espec铆fico var铆a seg煤n el motor de base de datos y el controlador que est茅 utilizando. Consulte la documentaci贸n de SQLAlchemy y la documentaci贸n de su proveedor de base de datos para obtener el formato de cadena de conexi贸n correcto.
- Creamos un `engine` usando `create_engine()`. El motor es responsable de administrar el grupo de conexiones y la comunicaci贸n con la base de datos. El par谩metro `echo=True` puede ser 煤til para la depuraci贸n, ya que imprimir谩 las sentencias SQL generadas en la consola.
- Definimos una clase base (`Base`) usando `declarative_base()`. Esto se utiliza como la clase base para todos nuestros modelos de SQLAlchemy.
- Definimos un modelo `User`, asign谩ndolo a una tabla de base de datos llamada `users`.
- Creamos la tabla en la base de datos usando `Base.metadata.create_all(engine)`.
- Creamos una clase de sesi贸n usando `sessionmaker(bind=engine)`. Esto configura la clase de sesi贸n para usar el motor especificado.
- Finalmente, instanciamos una sesi贸n usando `Session()`.
Comprendiendo las Transacciones
Una transacci贸n es una secuencia de operaciones de base de datos tratadas como una 煤nica unidad l贸gica de trabajo. Las transacciones se adhieren a las propiedades ACID:
- Atomicidad: Todas las operaciones en la transacci贸n tienen 茅xito por completo o fallan por completo. Si alguna parte de la transacci贸n falla, toda la transacci贸n se revierte.
- Consistencia: La transacci贸n debe mantener la base de datos en un estado v谩lido. No puede violar ninguna restricci贸n o regla de la base de datos.
- Aislamiento: Las transacciones concurrentes est谩n aisladas entre s铆. Los cambios realizados por una transacci贸n no son visibles para otras transacciones hasta que la primera transacci贸n se confirma.
- Durabilidad: Una vez que una transacci贸n se confirma, sus cambios son permanentes y sobrevivir谩n incluso a fallas del sistema.
SQLAlchemy proporciona mecanismos para gestionar transacciones, garantizando que se mantengan estas propiedades ACID.
Manejo b谩sico de transacciones
Las operaciones de transacci贸n m谩s comunes son commit (confirmar) y rollback (revertir).
Confirmaci贸n de transacciones
Cuando todas las operaciones dentro de una transacci贸n se han completado con 茅xito, se confirma la transacci贸n. Esto persiste los cambios en la base de datos.
try:
# Agregar un nuevo usuario
new_user = User(name='Alice Smith', email='alice.smith@example.com')
session.add(new_user)
# Confirmar la transacci贸n
session.commit()
print("隆Transacci贸n confirmada con 茅xito!")
except Exception as e:
# Manejar excepciones
print(f"Ocurri贸 un error: {e}")
session.rollback()
print("Transacci贸n revertida.")
finally:
session.close()
En este ejemplo:
- Agregamos un nuevo objeto `User` a la sesi贸n.
- Llamamos a `session.commit()` para persistir los cambios en la base de datos.
- Envolvemos el c贸digo en un bloque `try...except...finally` para manejar posibles excepciones.
- Si ocurre una excepci贸n, llamamos a `session.rollback()` para deshacer cualquier cambio realizado durante la transacci贸n.
- Siempre llamamos a `session.close()` en el bloque `finally` para liberar la sesi贸n y devolver la conexi贸n al grupo de conexiones. Esto es crucial para evitar fugas de recursos. No cerrar las sesiones puede provocar el agotamiento de las conexiones y la inestabilidad de la aplicaci贸n.
Reversi贸n de transacciones
Si ocurre alg煤n error durante una transacci贸n, o si decide que los cambios no deben persistir, revierte la transacci贸n. Esto revierte la base de datos a su estado anterior al inicio de la transacci贸n.
try:
# Agregar un usuario con un correo electr贸nico no v谩lido (ejemplo para forzar una reversi贸n)
invalid_user = User(name='Bob Johnson', email='invalid-email')
session.add(invalid_user)
# El commit fallar谩 si el correo electr贸nico no se valida a nivel de base de datos
session.commit()
print("Transacci贸n confirmada.")
except Exception as e:
print(f"Ocurri贸 un error: {e}")
session.rollback()
print("Transacci贸n revertida con 茅xito.")
finally:
session.close()
En este ejemplo, si agregar el `invalid_user` genera una excepci贸n (por ejemplo, debido a una violaci贸n de restricci贸n de la base de datos), la llamada `session.rollback()` deshacer谩 la inserci贸n intentada, dejando la base de datos sin cambios.
Gesti贸n avanzada de transacciones
Usando la declaraci贸n `with` para el alcance de las transacciones
Una forma m谩s pyth贸nica y robusta de gestionar las transacciones es usar la declaraci贸n `with`. Esto asegura que la sesi贸n se cierre correctamente, incluso si ocurren excepciones.
from contextlib import contextmanager
@contextmanager
def session_scope():
"""Proporcionar un 谩mbito transaccional en torno a una serie de operaciones."""
session = Session()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
# Uso:
with session_scope() as session:
new_user = User(name='Charlie Brown', email='charlie.brown@example.com')
session.add(new_user)
# Operaciones dentro del bloque 'with'
# Si no ocurren excepciones, la transacci贸n se confirma autom谩ticamente.
# Si ocurre una excepci贸n, la transacci贸n se revierte autom谩ticamente.
print("Usuario agregado.")
print("Transacci贸n completada (confirmada o revertida).")
La funci贸n `session_scope` es un gestor de contexto. Cuando entra en el bloque `with`, se crea una nueva sesi贸n. Cuando sale del bloque `with`, la sesi贸n se confirma (si no ocurrieron excepciones) o se revierte (si ocurri贸 una excepci贸n). La sesi贸n siempre se cierra en el bloque `finally`.
Transacciones anidadas (puntos de guardado)
SQLAlchemy admite transacciones anidadas usando puntos de guardado. Un punto de guardado le permite revertir a un punto espec铆fico dentro de una transacci贸n m谩s grande, sin afectar a toda la transacci贸n.
try:
with session_scope() as session:
user1 = User(name='David Lee', email='david.lee@example.com')
session.add(user1)
session.flush() # Enviar cambios a la base de datos pero a煤n no confirmar
# Crear un punto de guardado
savepoint = session.begin_nested()
try:
user2 = User(name='Eve Wilson', email='eve.wilson@example.com')
session.add(user2)
session.flush()
# Simular un error
raise ValueError("Error simulado durante la transacci贸n anidada")
except Exception as e:
print(f"Error de transacci贸n anidada: {e}")
savepoint.rollback()
print("Transacci贸n anidada revertida al punto de guardado.")
# Continuar con la transacci贸n externa, user1 a煤n se agregar谩
user3 = User(name='Frank Miller', email='frank.miller@example.com')
session.add(user3)
except Exception as e:
print(f"Error de transacci贸n externa: {e}")
#Commit confirmar谩 user1 y user3, pero no user2 debido a la reversi贸n anidada
try:
with session_scope() as session:
#Verificar que solo existan user1 y user3
users = session.query(User).all()
for user in users:
print(user)
except Exception as e:
print(f"Excepci贸n inesperada: {e}") #No deber铆a suceder
En este ejemplo:
- Iniciamos una transacci贸n externa usando `session_scope()`.
- Agregamos `user1` a la sesi贸n y vaciamos los cambios en la base de datos. `flush()` env铆a los cambios al servidor de la base de datos, pero *no* los confirma. Le permite ver si los cambios son v谩lidos (por ejemplo, no hay violaciones de restricciones) antes de confirmar toda la transacci贸n.
- Creamos un punto de guardado usando `session.begin_nested()`.
- Dentro de la transacci贸n anidada, agregamos `user2` y simulamos un error.
- Revertimos la transacci贸n anidada al punto de guardado usando `savepoint.rollback()`. Esto solo deshace los cambios realizados dentro de la transacci贸n anidada (es decir, la adici贸n de `user2`).
- Continuamos con la transacci贸n externa y agregamos `user3`.
- La transacci贸n externa se confirma, persistiendo `user1` y `user3` en la base de datos, mientras que `user2` se descarta debido a la reversi贸n del punto de guardado.
Control de los niveles de aislamiento
Los niveles de aislamiento definen el grado en que las transacciones concurrentes est谩n aisladas entre s铆. Los niveles de aislamiento m谩s altos proporcionan una mayor consistencia de los datos, pero pueden reducir la concurrencia y el rendimiento. SQLAlchemy le permite controlar el nivel de aislamiento de sus transacciones.
Los niveles de aislamiento comunes incluyen:
- Lectura no confirmada: El nivel de aislamiento m谩s bajo. Las transacciones pueden ver los cambios no confirmados realizados por otras transacciones. Esto puede conducir a lecturas sucias.
- Lectura confirmada: Las transacciones solo pueden ver los cambios confirmados realizados por otras transacciones. Esto evita las lecturas sucias, pero puede conducir a lecturas no repetibles y lecturas fantasma.
- Lectura repetible: Las transacciones pueden ver los mismos datos a lo largo de la transacci贸n, incluso si otras transacciones los modifican. Esto evita las lecturas sucias y las lecturas no repetibles, pero puede conducir a lecturas fantasma.
- Serializable: El nivel de aislamiento m谩s alto. Las transacciones est谩n completamente aisladas entre s铆. Esto evita las lecturas sucias, las lecturas no repetibles y las lecturas fantasma, pero puede reducir significativamente la concurrencia.
El nivel de aislamiento predeterminado depende del sistema de base de datos. Puede configurar el nivel de aislamiento al crear el motor o al comenzar una transacci贸n.
Ejemplo (PostgreSQL):
from sqlalchemy.dialects.postgresql import dialect
# Establecer el nivel de aislamiento al crear el motor
engine = create_engine('postgresql://user:password@host:port/database',
connect_args={'options': '-c statement_timeout=1000'} #Ejemplo de tiempo de espera
)
# Establecer el nivel de aislamiento al comenzar una transacci贸n (espec铆fico de la base de datos)
# Para postgresql, se recomienda configurarlo en la conexi贸n, no en el motor.
from sqlalchemy import event
from sqlalchemy.pool import Pool
@event.listens_for(Pool, "connect")
def set_isolation_level(dbapi_connection, connection_record):
existing_autocommit = dbapi_connection.autocommit
dbapi_connection.autocommit = True
cursor = dbapi_connection.cursor()
cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE")
dbapi_connection.autocommit = existing_autocommit
cursor.close()
# Entonces, las transacciones creadas a trav茅s de SQLAlchemy usar谩n el nivel de aislamiento configurado.
Importante: El m茅todo para configurar los niveles de aislamiento es espec铆fico de la base de datos. Consulte la documentaci贸n de su base de datos para obtener la sintaxis correcta. Configurar los niveles de aislamiento incorrectamente puede provocar un comportamiento inesperado o errores.
Gesti贸n de la concurrencia
Cuando varios usuarios o procesos acceden a los mismos datos de forma concurrente, es crucial gestionar la concurrencia correctamente para evitar la corrupci贸n de los datos y garantizar la consistencia de los datos. SQLAlchemy proporciona varios mecanismos para gestionar la concurrencia, incluido el bloqueo optimista y el bloqueo pesimista.
Bloqueo optimista
El bloqueo optimista asume que los conflictos son raros. Comprueba las modificaciones realizadas por otras transacciones antes de confirmar una transacci贸n. Si se detecta un conflicto, la transacci贸n se revierte.
Para implementar el bloqueo optimista, normalmente agrega una columna de versi贸n a su tabla. Esta columna se incrementa autom谩ticamente cada vez que se actualiza la fila.
from sqlalchemy import Column, Integer, String, Integer
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String)
content = Column(String)
version = Column(Integer, nullable=False, default=1)
def __repr__(self):
return f"<Article(title='{self.title}', version='{self.version}')>"
#Dentro del bloque try catch
def update_article(session, article_id, new_content):
article = session.query(Article).filter_by(id=article_id).first()
if article is None:
raise ValueError("Art铆culo no encontrado")
original_version = article.version
# Actualizar el contenido e incrementar la versi贸n
article.content = new_content
article.version += 1
# Intentar actualizar, comprobando la columna de versi贸n en la cl谩usula WHERE
rows_affected = session.query(Article).filter(
Article.id == article_id,
Article.version == original_version
).update({
Article.content: new_content,
Article.version: article.version
}, synchronize_session=False)
if rows_affected == 0:
session.rollback()
raise ValueError("Conflicto: el art铆culo ha sido actualizado por otra transacci贸n.")
session.commit()
En este ejemplo:
- Agregamos una columna `version` al modelo `Article`.
- Antes de actualizar el art铆culo, almacenamos el n煤mero de versi贸n actual.
- En la declaraci贸n `UPDATE`, incluimos una cl谩usula `WHERE` que verifica si la columna de versi贸n sigue siendo igual al n煤mero de versi贸n almacenado. `synchronize_session=False` evita que SQLAlchemy cargue el objeto actualizado de nuevo; estamos manejando expl铆citamente el versionado.
- Si la columna de versi贸n ha sido modificada por otra transacci贸n, la declaraci贸n `UPDATE` no afectar谩 a ninguna fila (rows_affected ser谩 0), y generaremos una excepci贸n.
- Revertimos la transacci贸n y notificamos al usuario que ha ocurrido un conflicto.
Bloqueo pesimista
El bloqueo pesimista asume que los conflictos son probables. Adquiere un bloqueo en una fila o tabla antes de modificarla. Esto evita que otras transacciones modifiquen los datos hasta que se libera el bloqueo.
SQLAlchemy proporciona varias funciones para adquirir bloqueos, como `with_for_update()`.
# Ejemplo usando PostgreSQL
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
# Configuraci贸n de la base de datos (reemplace con la URL real de su base de datos)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False) #Establezca echo en verdadero si desea ver el SQL generado
Base = declarative_base()
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(Integer)
def __repr__(self):
return f"<Item(name='{self.name}', value='{self.value}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
#Funci贸n para actualizar el elemento (dentro de un try/except)
def update_item_value(session, item_id, new_value):
# Adquirir un bloqueo pesimista en el elemento
item = session.query(Item).filter(Item.id == item_id).with_for_update().first()
if item is None:
raise ValueError("Elemento no encontrado")
# Actualizar el valor del elemento
item.value = new_value
session.commit()
return True
En este ejemplo:
- Usamos `with_for_update()` para adquirir un bloqueo en la fila `Item` antes de actualizarla. Esto evita que otras transacciones modifiquen la fila hasta que la transacci贸n actual se confirme o se revierta. La funci贸n `with_for_update()` es espec铆fica de la base de datos; consulte la documentaci贸n de su base de datos para obtener m谩s detalles. Algunas bases de datos pueden tener diferentes mecanismos de bloqueo o sintaxis.
Importante: El bloqueo pesimista puede reducir la concurrencia y el rendimiento, as铆 que 煤selo solo cuando sea necesario.
Mejores pr谩cticas de manejo de excepciones
El manejo adecuado de excepciones es fundamental para garantizar la integridad de los datos y evitar bloqueos de la aplicaci贸n. Siempre envuelva sus operaciones de base de datos en bloques `try...except` y maneje las excepciones de forma adecuada.
Aqu铆 hay algunas de las mejores pr谩cticas para el manejo de excepciones:
- Capturar excepciones espec铆ficas: Evite capturar excepciones gen茅ricas como `Exception`. Capture excepciones espec铆ficas como `sqlalchemy.exc.IntegrityError` o `sqlalchemy.exc.OperationalError` para manejar diferentes tipos de errores de manera diferente.
- Revertir transacciones: Siempre revierta la transacci贸n si ocurre una excepci贸n.
- Registrar excepciones: Registre las excepciones para ayudar a diagnosticar y solucionar problemas. Incluya la mayor cantidad de contexto posible en sus registros (por ejemplo, el ID de usuario, los datos de entrada, la marca de tiempo).
- Volver a generar excepciones cuando sea apropiado: Si no puede manejar una excepci贸n, vuelva a generarla para permitir que un manejador de nivel superior se encargue de ella.
- Limpiar recursos: Siempre cierre la sesi贸n y libere cualquier otro recurso en un bloque `finally`.
import logging
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.exc import IntegrityError, OperationalError
# Configurar el registro
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Configuraci贸n de la base de datos (reemplace con la URL real de su base de datos)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False)
Base = declarative_base()
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String)
price = Column(Integer)
def __repr__(self):
return f"<Product(name='{self.name}', price='{self.price}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
# Funci贸n para agregar un producto
def add_product(session, name, price):
try:
new_product = Product(name=name, price=price)
session.add(new_product)
session.commit()
logging.info(f"Producto '{name}' agregado con 茅xito.")
return True
except IntegrityError as e:
session.rollback()
logging.error(f"IntegrityError: {e}")
#Manejar las violaciones de restricciones de la base de datos (por ejemplo, nombre duplicado)
return False
except OperationalError as e:
session.rollback()
logging.error(f"OperationalError: {e}")
#Manejar errores de conexi贸n u otros problemas operativos
return False
except Exception as e:
session.rollback()
logging.exception(f"Ocurri贸 un error inesperado: {e}")
# Manejar cualquier otro error inesperado
return False
finally:
session.close()
En este ejemplo:
- Configuramos el registro para registrar eventos durante el proceso.
- Capturamos excepciones espec铆ficas como `IntegrityError` (para violaciones de restricciones) y `OperationalError` (para errores de conexi贸n).
- Revertimos la transacci贸n en los bloques `except`.
- Registramos las excepciones utilizando el m贸dulo `logging`. El m茅todo `logging.exception()` incluye autom谩ticamente el seguimiento de la pila en el mensaje de registro.
- Volvemos a generar la excepci贸n si no podemos manejarla.
- Cerramos la sesi贸n en el bloque `finally`.
Agrupaci贸n de conexiones de base de datos
SQLAlchemy utiliza la agrupaci贸n de conexiones para gestionar de forma eficiente las conexiones a la base de datos. Un grupo de conexiones mantiene un conjunto de conexiones abiertas a la base de datos, lo que permite a las aplicaciones reutilizar las conexiones existentes en lugar de crear otras nuevas para cada solicitud. Esto puede mejorar significativamente el rendimiento, especialmente en aplicaciones que manejan una gran cantidad de solicitudes concurrentes.
La funci贸n `create_engine()` de SQLAlchemy crea autom谩ticamente un grupo de conexiones. Puede configurar el grupo de conexiones pasando argumentos a `create_engine()`.
Los par谩metros comunes del grupo de conexiones incluyen:
- pool_size: El n煤mero m谩ximo de conexiones en el grupo.
- max_overflow: El n煤mero de conexiones que se pueden crear m谩s all谩 de pool_size.
- pool_recycle: El n煤mero de segundos despu茅s de los cuales se recicla una conexi贸n.
- pool_timeout: El n煤mero de segundos a esperar a que una conexi贸n est茅 disponible.
engine = create_engine('postgresql://user:password@host:port/database',
pool_size=5, #Tama帽o m谩ximo del grupo
max_overflow=10, #Desbordamiento m谩ximo
pool_recycle=3600, #Reciclar conexiones despu茅s de 1 hora
pool_timeout=30
)
Importante: Elija la configuraci贸n adecuada del grupo de conexiones en funci贸n de las necesidades de su aplicaci贸n y las capacidades de su servidor de base de datos. Un grupo de conexiones mal configurado puede provocar problemas de rendimiento o agotamiento de la conexi贸n.
Transacciones as铆ncronas (Async SQLAlchemy)
Para las aplicaciones modernas que requieren una alta concurrencia, especialmente aquellas construidas con frameworks as铆ncronos como FastAPI o AsyncIO, SQLAlchemy ofrece una versi贸n as铆ncrona llamada Async SQLAlchemy.
Async SQLAlchemy proporciona versiones as铆ncronas de los componentes principales de SQLAlchemy, lo que le permite realizar operaciones de base de datos sin bloquear el bucle de eventos. Esto puede mejorar significativamente el rendimiento y la escalabilidad de sus aplicaciones.
Aqu铆 hay un ejemplo b谩sico de c贸mo usar Async SQLAlchemy:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String
import asyncio
# Configuraci贸n de la base de datos (reemplace con la URL real de su base de datos)
db_url = 'postgresql+asyncpg://user:password@host:port/database'
engine = create_async_engine(db_url, echo=False)
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
async def create_db_and_tables():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def add_user(name, email):
async with AsyncSession(engine) as session:
new_user = User(name=name, email=email)
session.add(new_user)
await session.commit()
async def main():
await create_db_and_tables()
await add_user("Async User", "async.user@example.com")
if __name__ == "__main__":
asyncio.run(main())
Diferencias clave de SQLAlchemy s铆ncrono:
- `create_async_engine` se usa en lugar de `create_engine`.
- `AsyncSession` se usa en lugar de `Session`.
- Todas las operaciones de base de datos son as铆ncronas y deben esperarse usando `await`.
- Se deben usar controladores de base de datos as铆ncronos (por ejemplo, `asyncpg` para PostgreSQL).
Importante: Async SQLAlchemy requiere un controlador de base de datos que admita operaciones as铆ncronas. Aseg煤rese de tener el controlador correcto instalado y configurado.
Conclusi贸n
Dominar la gesti贸n de sesiones y transacciones de SQLAlchemy es esencial para crear aplicaciones de Python s贸lidas y confiables que interact煤an con bases de datos. Al comprender los conceptos de sesiones, transacciones, niveles de aislamiento y concurrencia, y al seguir las mejores pr谩cticas para el manejo de excepciones y la agrupaci贸n de conexiones, puede garantizar la integridad de los datos y optimizar el rendimiento de sus aplicaciones.
Ya sea que est茅 creando una peque帽a aplicaci贸n web o un sistema empresarial a gran escala, SQLAlchemy proporciona las herramientas que necesita para gestionar sus interacciones con la base de datos de forma eficaz. Recuerde priorizar siempre la integridad de los datos y manejar los posibles errores con elegancia para garantizar la fiabilidad de sus aplicaciones.
Considere explorar temas avanzados como:
- Compromiso de dos fases (2PC): Para transacciones que abarcan m煤ltiples bases de datos.
- Fragmentaci贸n: Para distribuir datos entre m煤ltiples servidores de bases de datos.
- Migraciones de bases de datos: Uso de herramientas como Alembic para gestionar los cambios de esquema de la base de datos.