Contenido del curso
Parámetros y Validación
CRUD en FastAPI
Arquitectura en FastAPI
Bases de Datos y Consultas
Middlewares
Unit Testing
Seguridad y Autenticación
Relaciones muchos a muchos con SQLModel
Resumen
Las relaciones muchos a muchos en bases de datos te permiten conectar registros de dos tablas a través de una tabla intermedia, abriendo la puerta a vínculos múltiples en ambas direcciones. Aquí aprenderás a modelarlas con SQLModel en un proyecto de FastAPI, usando un caso real: clientes que pueden tener varios planes y planes que pertenecen a varios clientes.
Qué es una relación muchos a muchos en bases de datos
Cuando un elemento de la tabla A puede asociarse con varios de la tabla B, y al revés, necesitas una tercera tabla que actúe como puente. Esa tabla intermedia guarda las referencias cruzadas entre ambos lados.
¿Qué es una tabla intermedia? Es una tabla que conecta dos modelos almacenando sus foreign keys. Permite que un customer tenga muchos plans y que un plan tenga muchos customers sin duplicar datos.
En el ejemplo del proyecto, un customer puede tener planes como premium o basic, y cada plan puede estar vinculado a múltiples clientes [00:21].
Cómo definir el modelo Plan en SQLModel
El primer paso ocurre en el archivo models.py, donde se declara la clase Plan heredando de SQLModel con table=True para que se cree físicamente en la base de datos [00:48].
Los campos del modelo son:
id: entero, opcional, primary key.name: string obligatorio.price: entero.description: string.
python class Plan(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) name: str price: int description: str
Este modelo representa un plan comercial que luego se conectará con los clientes a través del puente.
Cómo crear la tabla intermedia CustomerPlan
La tabla intermedia se llama CustomerPlan y también lleva table=True. Su trabajo es guardar las foreign keys que vinculan a Plan con Customer [01:25].
python class CustomerPlan(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) plan_id: int = Field(foreign_key="plan.id") customer_id: int = Field(foreign_key="customer.id")
La foreign key plan.id referencia directamente al modelo Plan, y customer.id apunta al modelo Customer ya existente. Así, cada fila representa una combinación cliente-plan.
¿Por qué necesito un id propio en la tabla intermedia? Porque SQLModel y Pydantic exigen una primary key en cada tabla. Sin ese
id, el proyecto lanza un error indicando que no encuentra clave primaria paraCustomerPlan[02:38].
Por qué se usa Field con foreign_key
El parámetro foreign_key le dice a la base de datos que ese campo apunta al id de otra tabla. Es lo que hace válida la relación a nivel relacional, no solo a nivel de objeto Python.
Cómo conectar los modelos con relationship y back_populates
A nivel de base de datos las llaves foráneas son suficientes, pero para que los objetos Python puedan navegar la relación necesitas declarar campos con Relationship en cada modelo [02:09].
En Plan, agregas el campo customers como una lista que apunta a Customer:
python customers: list["Customer"] = Relationship( back_populates="plans", link_model=CustomerPlan )
Y en Customer, agregas plans apuntando de vuelta a Plan:
python plans: list[Plan] = Relationship( back_populates="customers", link_model=CustomerPlan )
El back_populates indica el nombre de la variable espejo en la otra clase, y link_model declara cuál es la tabla puente. Si defines Plan arriba de Customer, puedes referenciarlo directamente; si está abajo, usas el nombre como string entre comillas.
Cómo verificar la tabla intermedia en SQLite
Una vez corriendo el proyecto, puedes abrir la terminal de SQLite y ejecutar .tables para listar todas las tablas creadas [03:13]. Verás que aparece customerplan junto a las otras.
Al revisar el esquema de esa tabla encontrarás tres columnas:
idcomo primary key.plan_idcomo foreign key haciaplan.id.customer_idcomo foreign key haciacustomer.id.
Eso confirma que la relación quedó bien establecida en ambos lados.
Qué errores comunes aparecen al usar SQLModel
Uno de los más frecuentes es olvidar la primary key en la tabla intermedia. Pydantic y SQLModel lo detectan al levantar la app y lanzan un mensaje claro indicando que falta esa clave en CustomerPlan [02:38].
La solución es simple: agregar un campo id: int | None = Field(default=None, primary_key=True) al modelo intermedio. Después de eso, el error desaparece y los modelos quedan listos para usarse en un endpoint.
¿Qué otros campos le agregarías a la tabla CustomerPlan que puedan ser útiles a futuro? Cuéntalo en los comentarios.