Gestión de Bases de Datos en Python con SQLAlchemy ORM

SQLAlchemy es un toolkit de SQL y un sistema de mapeo objeto-relacional (ORM) para Python que permite a los desarrolladores interactuar con bases de datos utilizando paradigmas de programación orientada a objetos. Su flexibilidad facilita la transición entre diferentes motores de bases de datos sin necesidad de reescribir gran parte del código.

Instalación del Entorno

Para comenzar a trabajar con SQLAlchemy, es necesario instalar el paquete principal a través de pip:

pip install sqlalchemy

Dependiendo del motor de base de datos que se pretenda utilizar, se requieren controladores adicionales:

# Para bases de datos PostgreSQL
pip install psycopg2

# Para bases de datos MySQL
pip install pymysql

# SQLite viene integrado en la biblioteca estándar de Python

Configuración de la Conexión

El primer paso consiste en establecer un motor de conexión (Engine) y una fábrica de sesiones para interactuar con la base de datos.

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# Configuración para una base de datos local SQLite
db_url = "sqlite:///gestion_proyectos.db"
motor = create_engine(db_url, echo=True)

# Creación de la sesión para transacciones
SessionApp = sessionmaker(bind=motor)
sesion = SessionApp()

Definición de Modelos y Relaciones

Utilizaremos la base declarativa para definir nuestras entidades. En este ejemplo, estableceremos una relación de uno a muchos entre un 'Departamento' y sus 'Empleados', y una relación de muchos a muchos entre 'Proyectos' y 'Tecnologías'.

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

Base = declarative_base()

# Tabla de asociación para la relación muchos a muchos
proyectos_tecnologias = Table(
    'asoc_proy_tec',
    Base.metadata,
    Column('proyecto_id', Integer, ForeignKey('proyectos.id')),
    Column('tecnologia_id', Integer, ForeignKey('tecnologias.id'))
)

class Departamento(Base):
    __tablename__ = 'departamentos'
    id = Column(Integer, primary_key=True)
    nombre = Column(String(50), nullable=False)
    
    empleados = relationship("Empleado", back_populates="departamento")

class Empleado(Base):
    __tablename__ = 'empleados'
    id = Column(Integer, primary_key=True)
    nombre_completo = Column(String(100), nullable=False)
    correo = Column(String(100), unique=True)
    dept_id = Column(Integer, ForeignKey('departamentos.id'))
    
    departamento = relationship("Departamento", back_populates="empleados")

class Proyecto(Base):
    __tablename__ = 'proyectos'
    id = Column(Integer, primary_key=True)
    titulo = Column(String(150))
    
    tecnologias = relationship("Tecnologia", secondary=proyectos_tecnologias, back_populates="proyectos")

class Tecnologia(Base):
    __tablename__ = 'tecnologias'
    id = Column(Integer, primary_key=True)
    nombre = Column(String(50), unique=True)
    
    proyectos = relationship("Proyecto", secondary=proyectos_tecnologias, back_populates="tecnologias")

# Generar las tablas en el motor definido
Base.metadata.create_all(motor)

Operaciones de Persistencia (CRUD)

Inserción de Registros

Podemos añadir registros individuales o múltiples de forma sencilla.

# Insertar un nuevo departamento
nuevo_dept = Departamento(nombre="Ingeniería de Software")
sesion.add(nuevo_dept)
sesion.commit()

# Inserción masiva de empleados
empleados_lista = [
    Empleado(nombre_completo="Ana García", correo="ana@tech.com", departamento=nuevo_dept),
    Empleado(nombre_completo="Luis Torres", correo="luis@tech.com", departamento=nuevo_dept)
]
sesion.add_all(empleados_lista)
sesion.commit()

Consulta de Datos

SQLAlchemy ofrece una sintaxis fluida para filtrar y recuperar información.

# Obtener todos los departamentos
todos_depts = sesion.query(Departamento).all()

# Filtrar por nombre y obtener el primero
ingenieria = sesion.query(Departamento).filter_by(nombre="Ingeniería de Software").first()

# Consultas con filtros complejos
empleados_filtrados = sesion.query(Empleado).filter(
    Empleado.nombre_completo.like("A%"),
    Empleado.correo.contains("tech.com")
).all()

Actualización y Eliminación

Para modificar registros, simplemente alteramos sus atributos y confirmamos la transacción.

# Actualizar
empleado_editar = sesion.query(Empleado).filter_by(id=1).one()
empleado_editar.correo = "nuevo_correo@tech.com"
sesion.commit()

# Borrar
empleado_eliminar = sesion.query(Empleado).filter_by(id=2).first()
if empleado_eliminar:
    sesion.delete(empleado_eliminar)
    sesion.commit()

Uso de Funciones de Agregación y Joins

Es común requerir datos de múltiples tablas o realizar cálculos sobre las columnas.

from sqlalchemy import func

# Contar empleados por departamento
resumen = sesion.query(
    Departamento.nombre, 
    func.count(Empleado.id)
).join(Empleado).group_by(Departamento.nombre).all()

# Inner Join entre Empleado y Departamento
registros = sesion.query(Empleado.nombre_completo, Departamento.nombre).join(Departamento).all()

Gestión de Transacciones y Contexto

Para asegurar que la base de datos mantenga su integridad, es vital manejar correctamente las excepciones y el cierre de conexinoes.

from contextlib import contextmanager

@contextmanager
def manejar_sesion():
    nueva_sesion = SessionApp()
    try:
        yield nueva_sesion
        nueva_sesion.commit()
    except Exception as error:
        nueva_sesion.rollback()
        print(f"Error detectado: {error}")
        raise
    finally:
        nueva_sesion.close()

# Ejemplo de uso con el gestor de contexto
with manejar_sesion() as db:
    tec = Tecnologia(nombre="Python")
    db.add(tec)

Recomendaciones Técnicas

  • Carga Anticipada (Eager Loading): Utilice joinedload para evitar el problema de las consultas N+1 al acceder a objetos relacionados.
  • Pool de Conexiones: Configure adecuadamente el tamaño del pool en entornos de producción para evitar el agotamiento de recursos.
  • Validación de Datos: Implemente lógica de validación antes de realizar el commit para prevenir inconsistencias en el esquema de la base de datos.
  • Migraciones: Para proyectos en evolución, considere heramientas como Alembic para gestionar los cambios en la estructura de las tablas.

Etiquetas: SQLAlchemy Python ORM SQL bases-de-datos

Publicado el 7-16 12:37