Volver al catálogo
Season 37 7 Episodios 28 min 2026

SQLAlchemy

v2.0 — Edición 2026. Un completo curso en audio sobre SQLAlchemy, que cubre tanto el Core como el ORM, diseñado para la versión 2.0 lanzada en 2026. Aprende a mapear tu dominio, estructurar tu aplicación, gestionar transacciones con la Session y ejecutar consultas de forma eficaz.

Bases de datos ORM Python Core
SQLAlchemy
Reproduciendo ahora
Click play to start
0:00
0:00
1
Los cimientos: ¿Qué es SQLAlchemy y el ORM?
Bienvenidos a SQLAlchemy. Presentamos la arquitectura principal, explicando la diferencia entre el Core centrado en el esquema y el ORM centrado en el dominio. Aprenderás la jerga fundamental y por qué necesitas un ORM.
4m 17s
2
El Engine: Tu puerta de entrada a la base de datos
Toda aplicación de SQLAlchemy comienza con el Engine. Aprende cómo establecer la conectividad, qué es el connection pooling y cómo los dialectos y las DBAPIs acortan la distancia con tu base de datos.
4m 34s
3
Mapeando el dominio: Declarative Base y modelos
Traduce tus clases de Python a tablas de bases de datos automáticamente. Cubrimos DeclarativeBase, los tipos Mapped y cómo mapped_column construye los metadatos de tu base de datos.
4m 28s
4
Diseño del proyecto: Estructurando tu aplicación
La organización del código importa. Aprende las mejores prácticas para estructurar un repositorio de proyecto de SQLAlchemy para que tu Engine, modelos y Sessions se mantengan limpios y fáciles de mantener.
3m 41s
5
La Session: Dominando el Unit of Work
Descubre el patrón Unit of Work a través de la Session del ORM. Aprende cómo añadir objetos, cuándo ocurren los flushes y cómo hacer commit de las transacciones a la perfección.
4m 07s
6
Consultando datos: El constructo Select moderno
Obtén tus datos exactamente como los necesitas. Exploramos el constructo unificado select() de SQLAlchemy 2.0, el filtrado con where() y la ejecución de consultas con la Session.
3m 35s
7
Conectando los puntos: Relationships y JOINs
Enlaza tus tablas sin problemas. Aprende a configurar relationships, usar back_populates y gestionar automáticamente los JOINs de SQL entre modelos relacionados.
4m 10s

Episodios

1

Los cimientos: ¿Qué es SQLAlchemy y el ORM?

4m 17s

Bienvenidos a SQLAlchemy. Presentamos la arquitectura principal, explicando la diferencia entre el Core centrado en el esquema y el ORM centrado en el dominio. Aprenderás la jerga fundamental y por qué necesitas un ORM.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 1 de 7. ¿Alguna vez has hecho un drop de una tabla sin querer o has destrozado una query por un error tipográfico en un string de raw SQL? Las queries concatenadas con strings son frágiles, difíciles de mantener y suponen un riesgo de seguridad. Este episodio aborda la solución: La base: ¿Qué es SQLAlchemy y el ORM? El principal problema al que se enfrentan los desarrolladores al crear aplicaciones respaldadas por bases de datos es que Python y las bases de datos relacionales piensan de manera diferente. Python utiliza objetos. Los objetos tienen estado, comportamiento y relaciones complejas. Las bases de datos relacionales utilizan tablas, columnas y filas. Se basan en primary y foreign keys. Insertar un objeto de Python en una tabla de base de datos requiere traducir su estado a un SQL statement. Para recuperarlo, necesitas parsear las filas y rellenar un nuevo objeto. A esta fricción se la llama object-relational impedance mismatch. Escribir strings de raw SQL para gestionar esta traducción es tedioso y propenso a errores. SQLAlchemy es un toolkit de SQL para Python diseñado para resolver exactamente este problema. A diferencia de algunas herramientas que intentan ocultar la base de datos tras un muro de abstracción, SQLAlchemy abraza SQL. Proporciona un workflow robusto y Pythonic que genera un SQL óptimo mientras te mantiene al control. La arquitectura se divide en dos partes distintas: el Core y el ORM. El Core de SQLAlchemy es la base. Está orientado a comandos y a esquemas. Esto significa que trabajas directamente con tablas, columnas y filas de la base de datos, pero lo haces usando objetos de Python en lugar de strings de texto raw. Si quieres seleccionar datos, llamas a un método select. Si quieres filtrar, encadenas un método where. Esto elimina el riesgo de errores de sintaxis por concatenación de strings y te protege contra el SQL injection. El Core es, esencialmente, un lenguaje de expresiones SQL. El ORM, u Object Relational Mapper, se sitúa por encima del Core. Mientras que el Core está orientado a esquemas, el ORM está orientado a estados y al dominio. Aquí está la clave. Con el ORM, dejas de pensar en tablas y filas, y empiezas a pensar en el modelo de dominio de tu aplicación. Defines una clase estándar de Python, por ejemplo, una clase user. Configuras el ORM para mapear esta clase a una tabla user en la base de datos, y mapear los atributos de la clase a las columnas de la tabla. Cuando haces una query a la base de datos usando el ORM, no obtienes de vuelta filas raw de datos. Obtienes instancias completas de tu clase user. El ORM gestiona la traducción de forma transparente. Y lo que es más importante, hace un seguimiento del estado de esos objetos. Si cambias un nombre de usuario en Python, el ORM detecta que el objeto ha sido modificado. Cuando le dices a la sesión de la base de datos que guarde tus cambios, el ORM averigua automáticamente los update statements correctos y los ejecuta a través del Core. Tú te centras en tus objetos de Python y el ORM se encarga de la sincronización de la base de datos. Lo más importante a recordar es que el ORM no es una caja negra que reemplaza al Core. Es una capa opcional construida completamente con componentes del Core, que te permite bajar sin problemas a la generación explícita de SQL siempre que la lógica de tu dominio requiera el máximo rendimiento. Si estos episodios te resultan útiles y quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon. Gracias por pasar unos minutos conmigo. Hasta la próxima, que te vaya bien.
2

El Engine: Tu puerta de entrada a la base de datos

4m 34s

Toda aplicación de SQLAlchemy comienza con el Engine. Aprende cómo establecer la conectividad, qué es el connection pooling y cómo los dialectos y las DBAPIs acortan la distancia con tu base de datos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 2 de 7. Quieres comunicarte con una base de datos, pero diferentes bases de datos hablan diferentes dialectos, y todos los drivers de Python tienen sus propias particularidades. Necesitas un componente único y unificado que gestione el connection pooling, la traducción de dialectos y la gestión de drivers sin que esos detalles se filtren en la lógica de tu aplicación. Ese componente es el Engine. El Engine es el punto de partida para cualquier aplicación SQLAlchemy. Actúa como un registro central y una fábrica de conexiones a la base de datos. Cuando tu aplicación necesita comunicarse con la base de datos, al final pasa por el Engine. Por debajo, el Engine mantiene un connection pool. Esto significa que mantiene una caché de conexiones activas a la base de datos, manteniéndolas abiertas y listas para usar. Reutilizar conexiones de un pool es mucho más rápido que negociar una conexión de red nueva cada vez que necesitas ejecutar una query. Para instanciar un Engine, utilizas una función llamada create engine. Esta función requiere una URL de base de datos. La URL es un string que proporciona la ubicación de la base de datos, las credenciales y dos elementos de configuración cruciales: el dialecto y la DBAPI. El dialecto es la familia de base de datos a la que apuntas. SQL es un estándar, pero cada engine de base de datos lo implementa de forma ligeramente diferente. El dialecto le indica a SQLAlchemy si está formateando queries para PostgreSQL, MySQL, Oracle o SQLite. Gestiona todas las variaciones específicas de cada proveedor para que tu código Python se mantenga consistente. Aquí está la clave. SQLAlchemy no se conecta directamente a tu base de datos. Siempre delega la comunicación de red real a un driver de Python de terceros. Este driver se conoce como la DBAPI. Si utilizas PostgreSQL, tu DBAPI podría ser psycopg2. Si utilizas SQLite, normalmente es pysqlite. La URL de la base de datos especifica ambos elementos juntos. Por ejemplo, la URL podría empezar con el string sqlite plus pysqlite. Esto le indica explícitamente al Engine que formatee las queries usando el dialecto SQLite y que las transmita usando el driver pysqlite. Veamos cómo configurar una base de datos SQLite en memoria. Este es un patrón muy común para testing porque la base de datos vive completamente en la RAM y desaparece cuando el programa termina. Llamas a la función create engine y le pasas tu string de URL. Para una base de datos en memoria, la URL es sqlite plus pysqlite dos puntos barra barra barra dos puntos memory dos puntos. Cuando ejecutas esa línea de código, se crea el objeto Engine, pero no se conecta inmediatamente a la base de datos. El Engine es lazy. Prepara la configuración y configura el connection pool, pero espera hasta la primera vez que le pides explícitamente que ejecute una tarea antes de contactar a la DBAPI para establecer una conexión. Durante el desarrollo, a menudo necesitas verificar exactamente qué está enviando SQLAlchemy a la base de datos. La función create engine acepta un parámetro opcional llamado echo. Si pones echo en true, el Engine hará un log de todos los statements SQL raw que genere directamente en tu standard output. Actúa como una herramienta de debugging integrada, permitiéndote ver la traducción exacta entre tus operaciones de Python y el SQL resultante. El Engine existe para abstraer la desordenada realidad de las conexiones de red y los drivers de bases de datos. Proporciona a tu aplicación una interfaz limpia y estable, asegurando que el resto de tu código nunca tenga que preocuparse por cómo se accede físicamente a la base de datos. Gracias por pasar el rato. Espero que hayas aprendido algo nuevo.
3

Mapeando el dominio: Declarative Base y modelos

4m 28s

Traduce tus clases de Python a tablas de bases de datos automáticamente. Cubrimos DeclarativeBase, los tipos Mapped y cómo mapped_column construye los metadatos de tu base de datos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 3 de 7. Escribes tus modelos de dominio en Python y luego tienes que escribir un montón de SQL aparte solo para crear las tablas que los contengan. Con el tiempo, estas dos definiciones separadas casi siempre se desincronizan. ¿Qué pasaría si tus clases de Python pudieran diseñar automáticamente el esquema de tu base de datos? Eso es exactamente lo que resolvemos hoy con Mapear el dominio: Declarative Base y Models. Antes de ver las clases en sí, tenemos que hablar sobre la metadata de la base de datos. En SQLAlchemy, la metadata es, a efectos prácticos, un catálogo estructural. Es un registro central de Python que almacena el blueprint exacto de tu base de datos. Rastrea cada tabla, cada columna y cada constraint que defines. Cada vez que mapeas una clase en SQLAlchemy, los detalles de esa clase se incorporan directamente a este catálogo único. Para conectar tus clases de Python a este catálogo de metadata, utilizas un constructo llamado DeclarativeBase. No utilizas DeclarativeBase directamente. En su lugar, creas tu propia clase base personalizada heredando de ella. A partir de ese momento, cada modelo que crees en tu aplicación heredará de tu clase base personalizada. En el instante en que una clase hereda de ella, SQLAlchemy registra silenciosamente ese nuevo modelo en el catálogo de metadata subyacente. Piensa en cómo creas un modelo User. Defines una clase de Python llamada User y heredas de tu clase base personalizada. Lo primero que defines dentro de esta clase es un atributo llamado tablename, escrito con doble guion bajo a cada lado. Le asignas a esto el string user_account. Esta asignación explícita le dice a SQLAlchemy exactamente en qué tabla de la base de datos subyacente se almacenarán estos objetos de Python. A continuación, estableces las columnas. SQLAlchemy 2.0 se basa en los type hints estándar de Python para hacer esto. Defines tus atributos usando una anotación especial llamada Mapped. Para un identificador, anotas tu atributo ID como Mapped conteniendo un integer. Para un nombre de usuario, lo anotas como Mapped conteniendo un string. Este type hint es estrictamente para Python. Le dice a tu IDE y a tu type checker qué datos esperar. Pero el motor de base de datos necesita instrucciones más específicas que un simple tipo de Python. Aquí es donde entra en juego una función llamada mapped_column. Le asignas mapped_column a tu atributo de clase, y dentro de sus paréntesis, configuras las reglas específicas de la base de datos. Para tu ID de usuario, llamas a mapped_column y le pasas un flag que lo marca explícitamente como la primary key. Para una columna de tipo string, podrías pasarle un límite máximo de caracteres o un flag que requiera que el campo sea único. Si tu type hint de Python proporciona todo el contexto que SQLAlchemy necesita, de hecho puedes omitir mapped_column por completo. SQLAlchemy inferirá una columna de base de datos básica y estándar directamente de la anotación Mapped. Pero para las primary keys, o cualquier constraint específico de la base de datos, mapped_column es estrictamente necesario. Aquí está la clave. Una vez escritas tus clases de Python, el diseño de tu esquema ya está terminado. El catálogo de metadata contiene ahora un mapa perfecto de tu dominio. Puedes llamar a un método llamado create_all en la metadata de tu clase base, pasándole tu motor de base de datos. SQLAlchemy examina tu clase User, lee las columnas mapeadas, las traduce instantáneamente a sentencias CREATE TABLE perfectamente formateadas, y las aplica. Exactamente el mismo código de Python que escribes para ejecutar tu aplicación se convierte en la única fuente de la verdad que construye la estructura de tu base de datos. Gracias por acompañarme. Espero que hayas aprendido algo nuevo.
4

Diseño del proyecto: Estructurando tu aplicación

3m 41s

La organización del código importa. Aprende las mejores prácticas para estructurar un repositorio de proyecto de SQLAlchemy para que tu Engine, modelos y Sessions se mantengan limpios y fáciles de mantener.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 4 de 7. Ya has escrito tus modelos, tu engine y tus queries. Pero meterlos todos en un solo archivo es la vía rápida hacia una pesadilla de mantenimiento y circular imports. La solución es el Project Layout: cómo estructurar tu aplicación. Cuando ves tutoriales, el código suele estar contenido en un único script. Ves el setup del engine, la base class, los modelos y las queries apilados de arriba a abajo. Ese diseño es genial para aprender la sintaxis, pero si intentas construir un sistema en producción de esa manera, se desmorona. En cuanto añades web routes o background workers, surgen los problemas. Si una web route necesita importar un user model, pero el user model está en el mismo archivo que inicia el application server o inicializa la conexión a la base de datos, Python se confunde. Acabas con circular imports y tu código se vuelve imposible de testear. Necesitas una estricta separation of concerns. El patrón estándar es dividir ese único script en distintos módulos según su función. Primero, creas un archivo dedicado, normalmente llamado database dot py. Este archivo tiene exactamente una responsabilidad, que es gestionar la conexión a la base de datos. Aquí es donde pones tu llamada a create engine y configuras tu session maker. Aquí no defines tablas ni ejecutas queries. Al aislar el engine, te aseguras de que tu aplicación solo cree un único connection pool. Cualquier otro módulo de tu proyecto puede importar el session maker desde este archivo de forma segura, sin activar accidentalmente la lógica de inicio de la aplicación. A continuación, mueves las definiciones de tus tablas a un archivo llamado models dot py. Este archivo contiene tu Declarative Base y todas tus mapped classes, como un objeto User o Address. No importa nada de tu archivo de base de datos. Esta es la parte que importa. Tus modelos definen la forma de tus datos, pero no saben cómo conectarse a la base de datos. Como models dot py no tiene dependencias del engine ni de la active session, puedes importar tus mapped classes en cualquier parte de tu aplicación sin preocuparte por los side effects. Si tu proyecto crece mucho, incluso podrías dividir los modelos en un directorio con archivos separados para diferentes dominios. Siempre que todos importen exactamente la misma instancia de Declarative Base, SQLAlchemy sabe que pertenecen a la misma metadata collection. Finalmente, necesitas un lugar donde ejecutar realmente tus queries. Mantienes la ejecución de las queries completamente separada de tus modelos y archivos de conexión. Normalmente, esto va en tus route handlers o en service files dedicados. Cuando una aplicación necesita datos, el service file importa el session maker de tu módulo de base de datos, y las mapped classes necesarias de tu módulo de modelos. Abre una sesión usando un context manager, ejecuta un select statement, recupera los objetos y deja que el context manager cierre la conexión de forma segura cuando termina el bloque. El service file actúa como coordinador. Estructurar tu proyecto de esta manera mantiene tu lógica de conexión aislada, tus esquemas de datos portables y tus operaciones de negocio limpias. La regla más importante que debes recordar es que tus data models nunca deben importar tu database engine. Eso es todo por este episodio. Gracias por escuchar, y sigue programando.
5

La Session: Dominando el Unit of Work

4m 07s

Descubre el patrón Unit of Work a través de la Session del ORM. Aprende cómo añadir objetos, cuándo ocurren los flushes y cómo hacer commit de las transacciones a la perfección.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 5 de 7. Imagina un área de staging donde puedes poner en cola docenas de cambios en la base de datos y hacer push de todos a la vez, o cancelarlos al instante. Ese es precisamente el problema que resuelve el concepto de este episodio: la Session, dominando el Unit of Work. La Session de SQLAlchemy es la principal forma en que tus objetos de Python se comunican con la base de datos. Funciona según un patrón llamado Unit of Work. Al usar la Session, no envías comandos individuales directamente a la base de datos. Interactúas con un workspace inteligente. La Session supervisa tus operaciones, recopila las adiciones, modificaciones y eliminaciones, y calcula la forma más eficiente de traducirlas a SQL en el momento oportuno. Dentro de este workspace se encuentra un mecanismo llamado Identity Map. Este es un diccionario interno que vincula la primary key de una fila de la base de datos con la dirección de memoria específica de tu objeto de Python. Si solicitas un usuario con el ID cinco, la Session consulta primero el Identity Map. Si el objeto ya está cargado, recibes exactamente la misma instancia de Python. Esto garantiza que nunca tengas dos objetos de Python distintos representando la misma fila de la base de datos al mismo tiempo. Veamos cómo insertar nuevos datos. Empiezas creando una instancia de tu clase User mapeada, dándole un nombre como Alice. En esta etapa, tu objeto se encuentra en un estado llamado transient. Existe únicamente en la memoria de Python. La base de datos no tiene ni idea de que existe, y la Session tampoco. Para vincular el objeto a tu workspace, lo pasas al método add de la Session. El objeto pasa a un estado llamado pending. La Session ha registrado tu intención de insertar este nuevo usuario, pero aún no ha enviado nada de SQL a través de la red. El objeto simplemente está esperando en la cola. Esto introduce la estricta diferencia entre un flush y un commit. Cuando llamas a flush en la Session, esta coge tu cola de pending y hace un push de los cambios a la transacción actual de la base de datos. Emite la sentencia SQL INSERT. La base de datos la ejecuta y asigna una primary key. De vuelta en tu código de Python, tu objeto de usuario se rellena instantáneamente con ese nuevo ID de la base de datos. El objeto ha pasado a un estado persistent. Aquí está la clave. Aunque el objeto sea persistent y tenga un ID, el cambio todavía no es permanente. La base de datos está aislando esta transacción. Supongamos que tu código de repente detecta un error, como un email no válido para el usuario al que le acabas de hacer flush. Como no has hecho commit, puedes hacer un rollback. La base de datos descarta la transacción por completo y no se guarda nada en las tablas reales. Cuando estás absolutamente seguro de que los datos son correctos, llamas a commit. Un commit finaliza la transacción y guarda los datos en el disco. Llamar a commit activa automáticamente un flush primero, por lo que generalmente no necesitas llamar a flush manualmente a menos que necesites específicamente leer una primary key generada antes de completar el resto de tu lógica. La Session actúa como un buffer entre la memoria de tu aplicación y el almacenamiento permanente, permitiéndote orquestar cambios de datos complejos de forma segura antes de consolidarlos en la realidad. Gracias por escuchar. Cuidaos todos.
6

Consultando datos: El constructo Select moderno

3m 35s

Obtén tus datos exactamente como los necesitas. Exploramos el constructo unificado select() de SQLAlchemy 2.0, el filtrado con where() y la ejecución de consultas con la Session.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 6 de 7. En versiones anteriores de SQLAlchemy, hacer queries era como trabajar con el cerebro dividido. Tenías que memorizar una sintaxis para las operaciones de Core y un conjunto de reglas completamente distinto para el ORM. Cada vez que escribías una query, tenías que pararte a pensar si estabas obteniendo raw rows u objetos mapeados. La versión 2.0 borró esa línea con la estructura moderna Select. El concepto es muy sencillo. Ahora hay una única función unificada llamada select. Le pasas directamente la entidad a la que quieres hacer la query. Si tienes una clase del ORM llamada User, le pasas la clase User a la función select. Esto crea un objeto select que representa una query contra la tabla user subyacente. Este objeto select es generativo. Llamar a la función select no ejecuta nada contra la base de datos. En su lugar, crea un expression object en memoria. Cuando llamas a un método en este objeto para añadir condiciones, te devuelve un objeto select nuevo con esas nuevas condiciones aplicadas. Esto te permite construir queries complejas de forma dinámica, paso a paso, pasando el objeto por tu aplicación antes de llegar a hablar con la base de datos. Eso cubre la query base. ¿Cómo la filtras? Le añades el método where a tu objeto select. Dentro del método where, usas operadores estándar de Python directamente sobre los atributos de tu clase mapeada. Por ejemplo, para encontrar un usuario específico, escribes la clase User punto name, seguido de dos signos igual, y el string de destino. SQLAlchemy intercepta esto. Sobrecarga los operadores estándar de Python, como el doble igual o el signo de mayor que. En lugar de evaluar la expresión a true o false en Python, la traduce a la sintaxis SQL correcta para la cláusula WHERE de la base de datos. Ahora tienes un query object completamente construido. Para recuperar tus datos realmente, le pasas este objeto a una session. Puedes pasarlo al método execute estándar de la session, pero eso devuelve un result object donde cada fila es esencialmente una tupla. Incluso si hiciste la query a una sola clase del ORM, tu objeto mapeado se queda atrapado dentro de una tupla de un solo elemento, y tienes que extraerlo manualmente al recibirlo. Aquí está la clave. Cuando quieras recuperar objetos del ORM, usa mejor el método scalars de la session. Le pasas tu objeto select a session punto scalars. La session ejecuta la query, extrae automáticamente esas tuplas del result set subyacente, y genera un iterable con tus objetos del ORM completamente poblados. Obtienes inmediatamente una colección limpia de instancias de User, listas para modificar o leer. La estructura select unificada significa que los mismos bloques de construcción de queries que usas para los objetos del ORM se pueden ejecutar directamente contra conexiones Core de bajo nivel, dejándote con exactamente un único modelo mental que mantener en toda tu capa de base de datos. Eso es todo por este episodio. ¡Gracias por escuchar, y sigue programando!
7

Conectando los puntos: Relationships y JOINs

4m 10s

Enlaza tus tablas sin problemas. Aprende a configurar relationships, usar back_populates y gestionar automáticamente los JOINs de SQL entre modelos relacionados.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. SQLAlchemy, episodio 7 de 7. Las bases de datos relacionales existen porque los datos se conectan, pero escribir joins complejos a mano cada vez que necesitas un registro relacionado cansa rápido. Acabas con lógica de query repetitiva dispersa por toda tu aplicación. Connecting the Dots: Relationships and Joins resuelve esto dejando que el object-relational mapper gestione las conexiones por ti. A nivel de base de datos, las tablas se conectan usando foreign keys. Si un usuario tiene varias direcciones, la tabla de direcciones tiene una columna que guarda el ID del usuario. Esa es una relación estándar de uno a muchos. Pero cuando escribes código en Python, no quieres extraer un ID y escribir una segunda query solo para encontrar esas direcciones. Quieres acceder a una propiedad en tu objeto de usuario y ver al instante una lista de objetos de dirección. Esto es exactamente lo que te da la estructura relationship. Cierra la brecha entre las foreign keys de la base de datos y los atributos de los objetos de Python. Para montar esto, necesitas dos cosas. Primero, declaras la foreign key en el lado de la base de datos. En tu modelo Address, defines una columna llamada user ID y la marcas explícitamente como una foreign key que apunta a la tabla User. Segundo, defines la relationship en el lado de Python. En tu modelo User, defines una propiedad llamada addresses, usando la función relationship, apuntando al modelo Address. También defines una propiedad relationship en el modelo Address que apunta de vuelta a User. Aquí está la clave. SQLAlchemy necesita saber que estas dos propiedades representan los dos lados de exactamente la misma conexión. Se lo dices usando el parámetro back populates en ambas definiciones de relationship. En el modelo User, la relationship addresses establece back populates al string con el nombre de la propiedad user. En el modelo Address, la relationship user establece back populates al string con el nombre de la propiedad addresses. Gracias a back populates, el ORM mantiene tus objetos de Python sincronizados en memoria. Si coges un objeto address y se lo asignas a un usuario, esa dirección aparece al instante en la lista de direcciones del usuario. El framework se encarga de llevar el registro antes de que hagas commit de nada en la base de datos. Una vez que tus objetos están enlazados, les haces una query. Imagina que ejecutas un statement select para buscar a una usuaria llamada Alice. La base de datos devuelve la fila de la usuaria, y tú obtienes un objeto. En este momento exacto, las direcciones de Alice no están cargadas. El ORM no las trae porque aún no se las has pedido. Cuando por fin accedes a la propiedad addresses en tu código, tal vez para iterar sobre los nombres de las calles, el ORM nota que faltan los datos. Pausa automáticamente tu programa, emite un segundo statement select a la base de datos para encontrar todas las direcciones con el ID de Alice, y rellena la lista. A esto se le llama lazy loading. Es el comportamiento por defecto porque evita que la aplicación traiga miles de filas relacionadas a la memoria a menos que sean estrictamente necesarias. Le haces una query a un usuario, accedes a sus propiedades, y el sistema navega sin problemas por las foreign keys y lanza las queries necesarias en segundo plano. El verdadero poder de la estructura relationship es que oculta la complejidad mecánica de los joins, dejándote navegar por toda una base de datos solo con interactuar con objetos de Python conectados. Para dominar estos conceptos, explora la documentación oficial y prueba a montar tus propios modelos de forma práctica. No dudes en visitar devstories dot eu para sugerir temas que quieras que cubramos en futuras series. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue creando!