SQLModel / SQLAlchemy
SQLAlchemy es el toolkit SQL y ORM fundamental de Python. SQLModel (del autor de FastAPI) superpone la validación Pydantic a los modelos SQLAlchemy para esquemas amigables con la API. Esta página orienta sobre lo esencial; la sección de bases de datos profundiza más.
Receta
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
engine = create_engine("sqlite:///heroes.db")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
session.add(Hero(name="Deadpond", secret_name="Dive"))
session.commit()
heroes = session.exec(select(Hero)).all()Cuándo recurrir a esto:
- Servicios FastAPI que persisten en Postgres/MySQL/SQLite
- Compartir tipos entre filas de base de datos y modelos de respuesta de API
- Consultas complejas que necesitan la potencia de expresiones de SQLAlchemy
- Scripts adyacentes a Django que aún necesitan control SQL crudo
Ejemplo Funcional
from __future__ import annotations
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Book(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
title: str
author: str
published_year: int
def seed(engine) -> None:
with Session(engine) as session:
session.add(Book(title="Neuromancer", author="Gibson", published_year=1984))
session.commit()
def list_recent(engine, min_year: int) -> list[Book]:
with Session(engine) as session:
statement = select(Book).where(Book.published_year >= min_year).order_by(Book.title)
return list(session.exec(statement))
if __name__ == "__main__":
engine = create_engine("sqlite:///:memory:")
SQLModel.metadata.create_all(engine)
seed(engine)
print(list_recent(engine, 1980))Lo que esto demuestra:
- Una sola clase sirve como tabla ORM y modelo validado
- SQLAlchemy 2.0
select()+session.exec - Sesión por unidad de trabajo con commit explícito
- SQLite en memoria para un ejemplo ejecutable
Profundización
Cómo Funciona
- Engine - Pool de conexiones a la URL de la base de datos.
- Session - Unidad de trabajo que rastrea objetos modificados;
commit/rollback. - SQLModel -
table=Trueregistra el mapeo ORM; sin él, la clase es un DTO solo Pydantic. - SQLAlchemy Core -
Table,insert(),update()cuando el ORM es demasiado pesado.
SQLModel vs SQLAlchemy Crudo
| Enfoque | Mejor para |
|---|---|
| SQLModel | CRUD de FastAPI, tipos compartidos de API/BD |
| SQLAlchemy declarativo | Dominios grandes, Core/ORM mixto |
| Django ORM | Monolitos de Django |
Notas de Python
# Modelos de lectura vs escritura en aplicaciones más grandes
class BookCreate(SQLModel):
title: str
author: str
class BookRead(BookCreate):
id: intErrores Comunes
create_allen producción - Sin historial de migraciones. Solución: Revisiones de Alembic.- Consultas N+1 - Carga perezosa en bucles. Solución:
selectinloado joins explícitos. - Suposiciones de SQLite en producción - Diferencias con el bloqueo/tipos de Postgres. Solución: probar contra el motor de producción en CI.
- Compartir sesión entre solicitudes - Condiciones de carrera. Solución: sesión por solicitud a través de la dependencia de FastAPI.
- Nullable sin
Optional- Las pistas de tipo mienten. Solución:str | Nonecoincide con columnas anulables. - Tabla gigante de SQLModel - La API expone columnas de BD. Solución: esquemas
Read/Createseparados.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Django ORM | Django es el framework web | Microservicio FastAPI independiente |
psycopg SQL crudo | Consultas de informes, COPY masivo | Modelo de dominio rico con relaciones |
| Tortoise ORM | Aplicaciones pequeñas con enfoque async | Se necesita el ecosistema maduro de SQLAlchemy |
| Peewee | Scripts diminutos | Pooling de conexiones a escala |
Preguntas Frecuentes
¿SQLModel o SQLAlchemy solo?
SQLModel para CRUD nuevo en FastAPI; recurra a patrones de SQLAlchemy cuando las consultas superen la ergonomía del ORM.
¿Cómo funcionan las migraciones?
Use Alembic autogenerate a partir de metadatos - vea Alembic.
¿Sesiones asíncronas?
AsyncSession de SQLAlchemy 2.0 con asyncpg - las rutas async de FastAPI no deben bloquearse en sesiones síncronas.
¿Cómo se pagina?
select(Book).offset(skip).limit(limit) - agregue una clave de ordenación estable; considere la paginación por clave de cursor para tablas grandes.
¿Carga de relaciones?
Defina campos Relationship(); use selectinload en la consulta para cargar ansiosamente.
¿SQLModel funciona con Pydantic v2?
Sí, en las pilas actuales fijadas en este cookbook - valide las notas de actualización al actualizar cualquiera de las bibliotecas.
¿Cómo se prueban el código de la BD?
Retrocesos de transacciones por fixture de prueba o SQLite :memory: con create_all por módulo de prueba.
¿Cadena de conexión desde el entorno?
Cargue DATABASE_URL a través de pydantic-settings.
¿Escape hatch de SQL crudo?
session.exec(text("SELECT ...")) para informes - aún así vincule parámetros, nunca use f-string para SQL.
¿Django y SQLAlchemy juntos?
Posible en migraciones híbridas pero incómodo - elija un ORM por contexto delimitado.
Relacionado
- Conceptos Básicos de Bases de Datos - cobertura profunda de la capa de datos
- Alembic - migraciones de esquemas
- Rutas Asíncronas y Bases de Datos de FastAPI - patrones de AsyncSession
- Modelos y ORM de Django - alternativa de framework
Versiones de la Pila: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.