Las relaciones en SQLAlchemy abarcan dos capas: la clave foránea representa integridad en la base y relationship() define navegación entre objetos. Confundir ambos papeles produce mappings difíciles de mantener.
from __future__ import annotations
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
class Cliente(Base):
__tablename__ = "cliente"
id: Mapped[int] = mapped_column(primary_key=True)
pedidos: Mapped[list[Pedido]] = relationship(back_populates="cliente")
class Pedido(Base):
__tablename__ = "pedido"
id: Mapped[int] = mapped_column(primary_key=True)
cliente_id: Mapped[int] = mapped_column(ForeignKey("cliente.id"))
cliente: Mapped[Cliente] = relationship(back_populates="pedidos")
Las anotaciones Mapped indican si un atributo es escalar, opcional o colección. back_populates conecta ambos lados. La nulabilidad de cliente_id debe coincidir con el tipo y la regla real del dominio.
Buenas prácticas
Define conscientemente el borrado en base de datos y ORM; no supongas cascadas. Elige eager loading si la consulta necesita relaciones y evita lazy loading accidental en bucles o código asíncrono. En muchos-a-muchos con campos extra, usa un objeto de asociación.
Consulta la guía de SQLAlchemy y, para I/O no bloqueante, SQLAlchemy asíncrono.
La documentación oficial de relaciones de SQLAlchemy, consultada el 22 de julio de 2026, detalla la API y sus límites.