SQLAlchemy ORM: Guía Completa para el Manejo de Bases de Datos en Python

SQLAlchemy es uno de los frameworks ORM (mapeo objeto-relacional) más populares en Python, ofreciendo una forma eficiente y flexible de operar con bases de datos. Este artículo presentará cómo utilizar el ORM de SQLAlchemy para realizar operaciones de base de datos.

Tabla de Contenido

  1. Instalación de SQLAlchemy
  2. Conceptos Fundaemntales
  3. Conexión a la Base de Datos
  4. Definición de Modelos de Datos
  5. Creación de Tablas en la Base de Datos
  6. Operaciones CRUD Básicas
  7. Consulta de Datos
  8. Operaciones con Relaciones
  9. Gestión de Transacciones
  10. Prácticas Recomendadas

Instalación

bash

pip install sqlalchemy

Para conectar con bases de datos específicas, se requieren controladores adicionales:

bash

# PostgreSQL
pip install psycopg2-binary

# MySQL
pip install mysql-connector-python

# SQLite (viene incluido en la biblioteca estándar de Python, no requiere instalación adicional)

Conceptos Fundamentales

  • Engine: Motor de conexión a la base de datos, encargado de la comunicación con la base de datos
  • Session: Sesión de base de datos, gestiona todas las operaciones de persistencia
  • Model: Clase de modelo de datos, corresponde a una tabla en la base de datos
  • Query: Objeto de consulta, utilizado para construir y ejecutar consultas a la base de datos

Conexión a la Base de Datos

python

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# Crear el motor de conexión a la base de datos
# Ejemplo con SQLite
motor = create_engine('sqlite:///ejemplo.db', echo=True)

# Ejemplo con PostgreSQL
# motor = create_engine('postgresql://usuario:contraseña@localhost:5432/mibasededatos')

# Ejemplo con MySQL
# motor = create_engine('mysql+mysqlconnector://usuario:contraseña@localhost:3306/mibasededatos')

# Crear fábrica de sesiones
FabricaSesion = sessionmaker(autocommit=False, autoflush=False, bind=motor)

# Crear instancia de sesión
sesion = FabricaSesion()

Definición de Modelos de Datos

python

from sqlalchemy import Column, Integer, String, ForeignKey, Float
from sqlalchemy.orm import relationship, declarative_base

# Crear clase base
Base = declarative_base()

class Cliente(Base):
    __tablename__ = 'clientes'
    
    id = Column(Integer, primary_key=True, index=True)
    nombre = Column(String(50), nullable=False)
    email = Column(String(100), unique=True, index=True)
    edad = Column(Integer)
    
    # Definir relación uno a muchos
    pedidos = relationship("Pedido", back_populates="cliente")
    
class Pedido(Base):
    __tablename__ = 'pedidos'
    
    id = Column(Integer, primary_key=True, index=True)
    fecha = Column(String(20), nullable=False)
    monto = Column(Float, nullable=False)
    cliente_id = Column(Integer, ForeignKey('clientes.id'))
    
    # Definir relación muchos a uno
    cliente = relationship("Cliente", back_populates="pedidos")
    
    # Definir relación muchos a muchos (a través de tabla intermedia)
    productos = relationship("Producto", secondary="detalle_pedido", back_populates="pedidos")

class Producto(Base):
    __tablename__ = 'productos'
    
    id = Column(Integer, primary_key=True, index=True)
    nombre = Column(String(30), unique=True, nullable=False)
    precio = Column(Float, nullable=False)
    stock = Column(Integer, default=0)
    
    pedidos = relationship("Pedido", secondary="detalle_pedido", back_populates="productos")

# Tabla intermedia (para relaciones muchos a muchos)
class DetallePedido(Base):
    __tablename__ = 'detalle_pedido'
    
    pedido_id = Column(Integer, ForeignKey('pedidos.id'), primary_key=True)
    producto_id = Column(Integer, ForeignKey('productos.id'), primary_key=True)
    cantidad = Column(Integer, nullable=False)

Creación de Tablas en la Base de Datos

python

# Crear todas las tablas
Base.metadata.create_all(bind=motor)

# Eliminar todas las tablas
# Base.metadata.drop_all(bind=motor)

Operaciones CRUD Básicas

Creación de Datos

python

# Crear nuevo cliente
nuevo_cliente = Cliente(nombre="Carlos Rodríguez", email="carlos@example.com", edad=30)
sesion.add(nuevo_cliente)
sesion.commit()

# Creación masiva
sesion.add_all([
    Cliente(nombre="Ana García", email="ana@example.com", edad=25),
    Cliente(nombre="López Martín", email="martin@example.com", edad=42)
])
sesion.commit()

Lectura de Datos

python

# Obtener todos los clientes
clientes = sesion.query(Cliente).all()

# Obtener el primer cliente
primer_cliente = sesion.query(Cliente).first()

# Obtener cliente por ID
cliente = sesion.query(Cliente).get(1)

Actualización de Datos

python

# Consultar y actualizar
cliente = sesion.query(Cliente).get(1)
cliente.nombre = "Carlos Rodríguez Sánchez"
sesion.commit()

# Actualización masiva
sesion.query(Cliente).filter(Cliente.nombre.like("Carlos%")).update({"nombre": "Carlos R."}, synchronize_session=False)
sesion.commit()

Eliminación de Datos

python

# Consultar y eliminar
cliente = sesion.query(Cliente).get(1)
sesion.delete(cliente)
sesion.commit()

# Eliminación masiva
sesion.query(Cliente).filter(Cliente.nombre == "Ana García").delete(synchronize_session=False)
sesion.commit()

Consulta de Datos

Consultas Básicas

python

# Obtener todos los registros
clientes = sesion.query(Cliente).all()

# Obtener campos específicos
nombres = sesion.query(Cliente.nombre).all()

# Ordenamiento
clientes = sesion.query(Cliente).order_by(Cliente.nombre.desc()).all()

# Limitar resultados
clientes = sesion.query(Cliente).limit(10).all()

# Paginación
clientes = sesion.query(Cliente).offset(5).limit(10).all()

Consultas con Filtros

python

from sqlalchemy import or_

# Filtro por igualdad
cliente = sesion.query(Cliente).filter(Cliente.nombre == "Carlos Rodríguez").first()

# Búsqueda con comodines
clientes = sesion.query(Cliente).filter(Cliente.nombre.like("Carlos%")).all()

# Consulta IN
clientes = sesion.query(Cliente).filter(Cliente.nombre.in_(["Carlos Rodríguez", "Ana García"])).all()

# Múltiples condiciones
clientes = sesion.query(Cliente).filter(
    Cliente.nombre == "Carlos Rodríguez", 
    Cliente.email.like("%@example.com")
).all()

# Condiciones OR
clientes = sesion.query(Cliente).filter(
    or_(Cliente.nombre == "Carlos Rodríguez", Cliente.nombre == "Ana García")
).all()

# Diferente de
clientes = sesion.query(Cliente).filter(Cliente.nombre != "Carlos Rodríguez").all()

Consultas de Agregación

python

from sqlalchemy import func

# Conteo
total_clientes = sesion.query(Cliente).count()

# Conteo con agrupamiento
pedidos_por_cliente = sesion.query(
    Cliente.nombre, 
    func.count(Pedido.id)
).join(Pedido).group_by(Cliente.nombre).all()

# Suma, promedio, etc.
edad_promedio = sesion.query(func.avg(Cliente.edad)).scalar()

Consultas con Join

python

# Inner join
resultados = sesion.query(Cliente, Pedido).join(Pedido).filter(Pedido.monto > 100).all()

# Left outer join
resultados = sesion.query(Cliente, Pedido).outerjoin(Pedido).all()

# Especificar condición de join
resultados = sesion.query(Cliente, Pedido).join(Pedido, Cliente.id == Pedido.cliente_id).all()

Operaciones con Relaciones

python

# Crear objetos con relaciones
cliente = Cliente(nombre="Sofía Pérez", email="sofia@example.com", edad=28)
pedido = Pedido(fecha="2023-05-15", monto=150.75, cliente=cliente)
sesion.add(pedido)
sesion.commit()

# Acceder a través de relaciones
print(f"El pedido '{pedido.fecha}' pertenece a {pedido.cliente.nombre}")
print(f"El cliente {cliente.nombre} tiene los siguientes pedidos:")
for p in cliente.pedidos:
    print(f"  - Fecha: {p.fecha}, Monto: {p.monto}")

# Operaciones con relaciones muchos a muchos
producto1 = Producto(nombre="Laptop", precio=850.00, stock=15)
producto2 = Producto(nombre="Mouse", precio=25.50, stock=50)

pedido.productos.append(producto1)
pedido.productos.append(producto2)
sesion.commit()

print(f"El pedido '{pedido.fecha}' contiene los siguientes productos:")
for producto in pedido.productos:
    print(f"  - {producto.nombre}: ${producto.precio}")

Gestión de Transacciones

python

# Transacción con commit automático
try:
    cliente = Cliente(nombre="Usuario de Prueba", email="prueba@example.com", edad=35)
    sesion.add(cliente)
    sesion.commit()
except Exception as e:
    sesion.rollback()
    print(f"Ocurrió un error: {e}")

# Usando gestores de contexto de transacción
from sqlalchemy.orm import Session

def crear_cliente(sesion: Session, nombre: str, email: str, edad: int):
    try:
        cliente = Cliente(nombre=nombre, email=email, edad=edad)
        sesion.add(cliente)
        sesion.commit()
        return cliente
    except:
        sesion.rollback()
        raise

# Transacciones anidadas
with sesion.begin_nested():
    cliente = Cliente(nombre="Cliente Transaccional", email="transaccional@example.com", edad=40)
    sesion.add(cliente)

# Puntos de guardado
punto_guardado = sesion.begin_nested()
try:
    cliente = Cliente(nombre="Cliente con Punto de Guardado", email="savepoint@example.com", edad=45)
    sesion.add(cliente)
    punto_guardado.commit()
except:
    punto_guardado.rollback()

Prácticas Recomendadas

  1. Gestión de Sesiones: Crear una nueeva sesión para cada petición y cerrarla al finalizar
  2. Manejo de Excepciones: Siempre manejar excepciones y realizar rollback de transacciones apropiadamente
  3. Carga Diferida: Prestar atención al problema de N+1 consultas, usar carga anticipada para optimizar
  4. Pool de Conexiones: Configurar adecuadamente el tamaño del pool de conexiones y tiempos de espera
  5. Validación de Datos: Verificar la integridad de los datos en la capa de modelo o aplicación

python

# Usar gestores de contexto para manejar sesiones
from contextlib import contextmanager

@contextmanager
def obtener_db():
    db = FabricaSesion()
    try:
        yield db
        db.commit()
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()

# Ejemplo de uso
with obtener_db() as db:
    cliente = Cliente(nombre="Usuario Contexto", email="contexto@example.com", edad=50)
    db.add(cliente)

Conclusión

El ORM de SQLAlchemy proporciona una forma poderosa y flexible de operar con bases de datos. A través de esta guía, debería ser capaz de:

  1. Instalar y configurar SQLAlchemy
  2. Definir modelos de datos y relaciones
  3. Realizar operaciones CRUD básicas
  4. Construir consultas complejas
  5. Gestoinar transacciones de base de datos
  6. Seguir las mejores prácticas

SQLAlchemy tiene muchas características avanzadas, como propiedades híbridas, escuchas de eventos, consultas personalizadas, entre otras, que vale la pena explorar y aprender.

Etiquetas: SQLAlchemy ORM Python bases de datos

Publicado el 5-30 20:09