Volver al catálogo
Season 38 8 Episodios 33 min 2026

Alembic Database Migrations

v1.18 — Edición 2026. Domina las migraciones de bases de datos con Alembic 1.18 en Python. Aprende a gestionar cambios de esquema, usar autogenerate, manejar restricciones, escribir scripts offline y orquestar migraciones de bases de datos de forma efectiva junto a SQLAlchemy.

Bases de datos Migraciones de bases de datos ORM
Alembic Database Migrations
Reproduciendo ahora
Click play to start
0:00
0:00
1
El porqué de las migraciones
Descubre por qué la gestión manual de esquemas falla a gran escala y cómo Alembic aporta el control de versiones a tu base de datos relacional. Exploramos el modelo mental central de las migraciones de bases de datos y desglosamos la anatomía del entorno de Alembic.
3m 36s
2
Anatomía de una revisión
Recorre el ciclo de vida de tu primera migración en Alembic. Desglosamos las funciones upgrade y downgrade, y revelamos cómo funciona realmente el seguimiento de versiones dentro de la base de datos.
4m 12s
3
La magia y los límites de autogenerate
Descubre cómo Alembic detecta cambios automáticamente comparando tus modelos de SQLAlchemy con los metadatos en vivo de la base de datos. Aprende qué detecta a la perfección y qué se le escapa.
3m 52s
4
La importancia de nombrar las restricciones
Descubre por qué depender de nombres generados por la base de datos para las restricciones es una receta para el desastre en las migraciones. Aprende a configurar una convención de nomenclatura unificada para tu sistema.
4m 18s
5
Migraciones offline y generación de SQL
Explora cómo generar scripts SQL puros para tus administradores de bases de datos en lugar de ejecutar Python directamente contra tu base de datos en producción. Analizamos el flujo de ejecución offline.
4m 42s
6
Migraciones batch para SQLite
Afronta el reto de alterar tablas en SQLite, que carece de soporte completo para ALTER TABLE. Aprende el flujo de trabajo 'move and copy' usando las operaciones batch de Alembic.
4m 02s
7
Trabajando con ramas
Domina la colaboración en equipo manejando flujos de migración ramificados. Aprende a identificar y fusionar historiales de revisión divergentes cuando múltiples desarrolladores modifican la base de datos.
3m 44s
8
Mejoras para producción
Sube de nivel tus conocimientos de Alembic con técnicas avanzadas. Cubrimos la invocación programática de comandos y cómo compartir una conexión con frameworks de aplicaciones como FastAPI.
5m 00s

Episodios

1

El porqué de las migraciones

3m 36s

Descubre por qué la gestión manual de esquemas falla a gran escala y cómo Alembic aporta el control de versiones a tu base de datos relacional. Exploramos el modelo mental central de las migraciones de bases de datos y desglosamos la anatomía del entorno de Alembic.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de bases de datos con Alembic, episodio 1 de 8. Ejecutas un comando manual de base de datos directamente en tu servidor de producción para añadir una sola columna y, de repente, la aplicación se bloquea. Tu código deployado esperaba una estructura, la base de datos ahora tiene otra, y no hay un botón de deshacer sencillo. Por eso hoy analizamos el caso a favor de las migraciones. Cuando un proyecto de software acaba de empezar, cambiar el esquema de la base de datos es fácil. Haces drop de las tablas y las vuelves a crear. Una vez que tienes usuarios reales y datos reales, eso ya no es una opción. Hacer cambios manuales en diferentes entornos como development, staging y producción acaba provocando un desajuste. El código de tu aplicación depende de un estado específico de la base de datos. Cuando ese estado se desvía, la aplicación falla. Alembic soluciona esto actuando como control de versiones para el esquema de tu base de datos. Igual que haces un seguimiento del historial de tu código fuente, Alembic hace un seguimiento del historial de la estructura de tu base de datos. Para usar Alembic, inicializas un entorno de migración. Esta es una estructura de directorios dedicada a la que haces commit en tu repositorio de control de versiones junto con el código de tu aplicación. Contiene las instrucciones y la configuración necesarias para modificar tu base de datos a lo largo del tiempo. El entorno consta de tres piezas principales. La primera es el archivo de configuración raíz, llamado alembic punto ini. Este archivo vive en la base de tu proyecto. Almacena la configuración básica, principalmente la URL de conexión a la base de datos, diciéndole a Alembic dónde está realmente la base de datos de destino. La siguiente es el directorio versions. Aquí es donde se almacenan los scripts de migración. Cada vez que necesitas cambiar el esquema de la base de datos, se crea un nuevo script de Python en esta carpeta. Cada script define dos acciones: una función upgrade para aplicar el cambio, y una función downgrade para revertirlo. Si necesitas añadir una tabla para perfiles de usuario, la instrucción exacta para ese cambio vive en un script justo aquí. La última pieza es un archivo llamado env punto py. Es fácil confundir esto con un archivo de configuración general de la aplicación o un lugar para almacenar variables del sistema, pero ese no es su propósito. Aquí está la clave. El archivo env punto py hace específicamente de puente entre los modelos de tu aplicación y el engine de migraciones de Alembic. Configura el engine de la base de datos, gestiona el ciclo de vida de la conexión y, lo más importante, carga tu metadata de SQLAlchemy. Esto le dice a Alembic exactamente cómo son tus modelos en el código, para que sepa con qué esquema de base de datos tiene que coincidir al final. Cada vez que invocas un comando de Alembic, primero ejecuta este script env punto py para establecer el contexto que necesita para operar. En lugar de depender de la frágil memoria de los comandos manuales de base de datos, tienes un proceso estructurado y repetible. El verdadero valor del entorno de Alembic no es solo que ejecuta comandos de forma segura, sino que crea un historial definitivo y versionado de cómo han evolucionado tus estructuras de datos exactamente desde el primer día. Si te gusta el podcast y quieres apoyar el programa, puedes encontrarnos buscando DevStoriesEU en Patreon. Me gustaría tomarme un momento para darte las gracias por escucharnos, nos ayuda muchísimo. ¡Que tengas un buen día!
2

Anatomía de una revisión

4m 12s

Recorre el ciclo de vida de tu primera migración en Alembic. Desglosamos las funciones upgrade y downgrade, y revelamos cómo funciona realmente el seguimiento de versiones dentro de la base de datos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de base de datos con Alembic, episodio 2 de 8. ¿Alguna vez te has preguntado cómo una base de datos sabe realmente qué versión de schema está ejecutando en este momento? No inspecciona tus tablas ni lo adivina basándose en las columnas que existen. El secreto reside en una única tabla oculta, y comprender cómo se conecta con tu código es la base de la anatomía de una revision. Para cambiar el schema de una base de datos, primero creas un script de migración. Esto lo haces ejecutando el comando revision de Alembic junto con un breve mensaje descriptivo, como create account table. Alembic genera un nuevo archivo Python en tu directorio versions. El nombre del archivo comienza con un string aleatorio de caracteres, seguido de tu mensaje. Ese string es un GUID parcial, un identificador único global. Alembic utiliza estos identificadores en lugar de números enteros secuenciales para evitar merge conflicts cuando varios desarrolladores están creando migraciones en diferentes branches al mismo tiempo. Si abres ese nuevo archivo Python, verás dos variables cerca del principio: revision y down revision. La variable revision contiene el GUID para este script específico. La variable down revision contiene el GUID del script que vino justo antes. Aquí está la clave. Los desarrolladores suelen pensar que las migraciones se aplican en orden, basándose en los timestamps de creación de los archivos o en el orden alfabético de sus nombres. No es así. Alembic se basa estrictamente en la chain de down revision. Lee estas variables dentro de los archivos para construir una linked list del historial de tu schema. Si un script no apunta a una revision anterior válida, la chain se rompe. Debajo de estas variables de enrutamiento, encontrarás dos funciones vacías: upgrade y downgrade. Aquí es donde escribes manualmente tus cambios de schema. Para nuestro escenario, estamos añadiendo una tabla de cuentas. En la función upgrade, escribes la lógica para crear la tabla, definiendo tus columnas, como una primary key de tipo integer y un string para el nombre de la cuenta. La función downgrade debe hacer exactamente lo contrario. Si upgrade crea la tabla de cuentas, downgrade debe hacerle un drop. Cada paso hacia adelante debe tener un paso hacia atrás correspondiente y fiable. Una vez que tu script está escrito, lo aplicas ejecutando el comando upgrade de Alembic, apuntándolo a head, lo que significa la revision más reciente en tu chain. Esto es lo que sucede entre bastidores. Alembic se conecta a tu base de datos y busca una tabla llamada alembic version. Si esta es tu primera migración, la tabla aún no existe, por lo que Alembic la crea. Esta tabla tiene exactamente una fila y una columna, que almacena el GUID de la revision aplicada actualmente. Alembic comprueba esta tabla, ve en qué punto se encuentra la base de datos actualmente y ejecuta las funciones upgrade de cada script necesario para alcanzar la revision objetivo. Finalmente, actualiza la tabla version con tu nuevo GUID. Si pruebas tu nueva funcionalidad de cuenta y te das cuenta de que algo va mal, puedes hacer un rollback limpiamente. Ejecutas el comando downgrade de Alembic, pasándole un identificador relativo como menos uno para retroceder una única revision. Alembic mira la versión actual en la base de datos, encuentra el script correspondiente y ejecuta su función downgrade. Hace un drop de la tabla de cuentas y actualiza la tabla version con el GUID anterior. La conclusión más importante es que un script de migración no es solo una colección suelta de comandos de base de datos. Es un nodo independiente en una linked list que le da a tu base de datos una ruta precisa para moverse tanto hacia adelante como hacia atrás en el tiempo. Gracias por pasar unos minutos conmigo. Hasta la próxima, cuídate.
3

La magia y los límites de autogenerate

3m 52s

Descubre cómo Alembic detecta cambios automáticamente comparando tus modelos de SQLAlchemy con los metadatos en vivo de la base de datos. Aprende qué detecta a la perfección y qué se le escapa.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de base de datos con Alembic, episodio 3 de 8. Confiar ciegamente en una herramienta automatizada para escribir las migraciones de tu base de datos es el camino más rápido para borrar accidentalmente tus tablas en producción. El problema radica en comprender qué ve realmente la herramienta al comparar tu código con la realidad, y eso es precisamente lo que abordamos hoy en La magia y los límites de autogenerate. Alembic tiene una feature principal llamada autogenerate. Cuando ejecutas un comando revision con el flag autogenerate, Alembic realiza una comparación. Primero, se conecta a tu base de datos activa e inspecciona el schema actual. Segundo, analiza el estado objetivo definido por los modelos de SQLAlchemy en el código de tu aplicación. Compara estos dos estados y determina las diferencias. Hay un malentendido muy común sobre este paso. Autogenerate no aplica mágicamente estos cambios a tu base de datos. Simplemente escribe el borrador de un script de Python que contiene las operaciones de migración que cree que necesitas para que la base de datos coincida con tus modelos. Tienes que revisar este script candidato antes de ejecutarlo realmente contra tu base de datos. Al comparar tu base de datos con tus modelos, autogenerate detecta de forma fiable los cambios estructurales básicos. Si añades una nueva model class a tu código, Alembic redacta una instrucción para crear una tabla. Si eliminas una model class, redacta una instrucción para hacer un drop de esa tabla. Detecta correctamente cuando añades o eliminas columnas, cuando cambias una columna para permitir valores null, o cuando añades índices básicos y unique constraints. Para estas operaciones estándar, esta feature te ahorra escribir muchísimo código a mano. Aquí viene la parte importante. Autogenerate tiene puntos ciegos porque no puede leerte la mente. Supón que decides renombrar una tabla existente en tus modelos de SQLAlchemy. Actualizas el código y ejecutas el comando autogenerate, esperando que Alembic prepare un comando seguro para alterar el nombre de la tabla. En su lugar, propone algo muy peligroso. Prepara un comando para hacer un drop de la tabla antigua por completo, destruyendo todos los datos de su interior, y luego prepara un segundo comando para crear una tabla totalmente nueva con el nuevo nombre. Alembic hace esto porque solo ve que el nombre de la tabla antigua existe en la base de datos pero falta en tus modelos, y que un nuevo nombre de tabla existe en tus modelos pero falta en la base de datos. No tiene forma de vincular ambas cosas como un simple rename. Tienes que editar manualmente el script generado para usar una operación rename table en su lugar. Exactamente la misma limitación se aplica al renombrar columnas. Autogenerate interpretará una columna renombrada como una instrucción para hacer un drop de la antigua y añadir una nueva. Más allá de los renames, hay cambios que autogenerate ignorará por completo por defecto. Si cambias el data type de una columna, o si modificas un valor server default, Alembic pasará por alto esas diferencias. Puedes configurar la herramienta para que detecte cambios de tipo y de default, pero tienes que activar explícitamente esos ajustes en la configuración de tu entorno. Incluso con esos ajustes activados, nunca detectará cambios en objetos sequence o en nombres de constraints. La forma más segura de tratar autogenerate es como una herramienta de dictado de alta velocidad que se encarga del boilerplate por ti, en lugar de un sistema inteligente que entiende la intención detrás de los cambios en tu código. Gracias por estar ahí. Espero que hayas aprendido algo nuevo.
4

La importancia de nombrar las restricciones

4m 18s

Descubre por qué depender de nombres generados por la base de datos para las restricciones es una receta para el desastre en las migraciones. Aprende a configurar una convención de nomenclatura unificada para tu sistema.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de base de datos con Alembic, episodio 4 de 8. La forma más fácil de arruinar un deploy es intentar eliminar una constraint de base de datos a la que nunca le pusiste nombre. Tu script de migración funciona perfectamente en tu máquina local, pero el entorno de staging se acaba de caer, quejándose de que falta una foreign key con un nombre impredecible como SYS C 0 0 2 9 3 3 4. El culpable es depender de identificadores generados por la base de datos. Este episodio trata sobre la importancia de nombrar las constraints y cómo automatizarlo. Muchos desarrolladores asumen que el object-relational mapper gestiona la eliminación de columnas y sus reglas asociadas sin problemas. Definen una unique constraint o una foreign key en su modelo, omiten el nombre para ahorrar tiempo y siguen adelante. Cuando haces esto, el motor de la base de datos toma el control. Sistemas como Postgres u Oracle autoasignan un nombre arbitrario generado por el sistema para aplicar esa regla. Esto crea una bomba de relojería para futuras migraciones. Cuando finalmente necesitas alterar o eliminar esa tabla o columna, Alembic utiliza la operación drop constraint. Esa operación requiere estrictamente el nombre exacto de la constraint de destino. Si dejas que la base de datos genere el nombre, es casi seguro que será diferente en desarrollo que en staging o producción. Acabas hardcodeando el nombre de una constraint local en tu script de migración, que falla inmediatamente al ejecutarse contra un entorno diferente donde esa string aleatoria no existe. Para solucionar esto, cada constraint en tu base de datos debe tener un nombre explícito y determinista. Hacer esto a mano en cientos de modelos es tedioso y fácil de olvidar. El mejor enfoque es configurar un diccionario de convenciones de nomenclatura en tu objeto MetaData de SQLAlchemy. Este diccionario sirve como plantilla global para tu aplicación. Defines reglas para cada tipo de constraint. Por ejemplo, puedes especificar que cada index debe nombrarse usando el prefijo i x, seguido del nombre de la tabla, seguido del nombre de la columna. Estableces plantillas similares para unique constraints, check constraints y foreign keys. Luego, adjuntas este objeto MetaData configurado a tu clase declarative base. Aquí viene la parte importante. Una vez que este diccionario está en su sitio, Alembic integra automáticamente tus convenciones de nomenclatura tanto en su función autogenerate como en sus operaciones manuales. Cuando ejecutas un comando para hacer un autogenerate de una nueva migración, Alembic analiza tus modelos, detecta una nueva constraint y consulta el diccionario MetaData. Aplica tu plantilla, calcula el nombre explícito y escribe esa string exacta en el script de Python generado. Dado que el script generado ordena explícitamente a la base de datos que utilice ese nombre específico, la constraint será idéntica en absolutamente todos los entornos. Esta integración se extiende a las operaciones de Alembic que se ejecutan durante el propio proceso de upgrade. Si un script de migración incluye una operación create table o add column con constraints inline que carecen de nombres explícitos, Alembic no las pasa a ciegas a la base de datos. Las intercepta, consulta la plantilla de convenciones de nomenclatura y asigna el nombre determinista correcto antes de ejecutar los comandos de la base de datos. Una convención de nomenclatura determinista garantiza que una regla creada en tu máquina local compartirá exactamente el mismo identificador cuando llegue a tus servidores de producción, eliminando por completo el riesgo de tener constraints imposibles de eliminar. Gracias por escuchar. Cuidaos todos.
5

Migraciones offline y generación de SQL

4m 42s

Explora cómo generar scripts SQL puros para tus administradores de bases de datos en lugar de ejecutar Python directamente contra tu base de datos en producción. Analizamos el flujo de ejecución offline.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de bases de datos con Alembic, episodio 5 de 8. En entornos empresariales altamente regulados, los desarrolladores casi nunca tienen acceso directo para ejecutar Python en bases de datos de producción. Cuando llega el día del deploy, los administradores de bases de datos suelen bloquearte el acceso y exigir un script raw y revisable en su lugar. Las Offline Migrations y la generación de SQL hacen de puente entre tu codebase de Python y su estricto proceso de deploy. Normalmente, Alembic se conecta a una base de datos en vivo y ejecuta los cambios de esquema directamente a través de esa conexión. Pero cuando necesitas entregar un archivo de texto plano a un equipo de DBAs, utilizas el modo offline. Al añadir la flag guion guion sql a tus comandos de upgrade o downgrade en la terminal, Alembic cambia por completo su comportamiento de ejecución. En lugar de ejecutar las sentencias contra un engine de base de datos, las renderiza como un string continuo de SQL estándar y las vuelca directamente al standard output. Puedes redirigir fácilmente este output de la terminal a un archivo de texto. Este comportamiento dual no es magia, está definido explícitamente en el archivo de environment de tu proyecto, normalmente llamado env punto py. Si miras dentro de ese archivo, encontrarás dos funciones de routing distintas. La primera es run migrations online. Esta función crea un engine de base de datos en vivo, vincula una conexión activa al context de Alembic, y ejecuta tus scripts de migración paso a paso. La segunda función es run migrations offline, y aquí es donde ocurre la traducción. Cuando pasas la flag sql, Alembic detecta la flag y, en su lugar, activa esta función offline. Configura el context usando únicamente una URL de base de datos. No se hace ninguna conexión de red, y no se instancia ningún engine. A continuación, toma tus estructuras de migración de Python y genera las sentencias CREATE, ALTER o DROP exactas, las encapsula en bloques de transacción BEGIN y COMMIT estándar, y formatea todo para el dialecto específico de tu base de datos. Esta es la clave. Como el modo offline nunca se conecta realmente a la base de datos, tus scripts de migración no pueden depender del estado activo de la base de datos. No puedes ejecutar una sentencia SELECT dentro de una migración offline para comprobar si existe una fila, y no puedes inspeccionar el estado actual de una tabla antes de hacer un cambio. Si tu código Python espera que un cursor de base de datos devuelva datos para decidir qué cambio de esquema hacer, la generación offline fallará. El script debe ser puramente declarativo. Simplemente le indica a Alembic qué estructuras generar. Imagina a un desarrollador terminando una feature branch local. Ha ejecutado las migraciones localmente en modo online para verificar que todo funciona correctamente contra su base de datos de prueba. Para la release de producción, ejecuta el comando upgrade con una revisión de inicio y fin específicas, añade la flag sql, y redirige el output a un archivo de texto. El resultado es un script SQL limpio y secuencial. El desarrollador entrega este archivo al equipo de DBAs. Los DBAs pueden leerlo, verificarlo según sus estrictas políticas de seguridad, y aplicarlo durante la ventana de mantenimiento utilizando herramientas estándar de administración de bases de datos. También tienes control sobre cómo se genera este output offline. Dentro de la función run migrations offline, la llamada a context configure acepta parámetros que ajustan el SQL renderizado. Un requisito común es convertir variables en valores literales. Al habilitar literal binds en la configuración, te aseguras de que cualquier dato insertado durante la migración incluya los valores reales directamente en el string de SQL, en lugar de generar un output con marcadores de parámetros genéricos. Esto asegura que el output sea un script completamente autónomo, listo para su ejecución. El verdadero valor de la generación offline es la previsibilidad; transforma los cambios dinámicos de estado de Python en SQL estático y auditable que cualquier pipeline de deploy o equipo de seguridad puede verificar antes de que se modifique una sola tabla. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!
6

Migraciones batch para SQLite

4m 02s

Afronta el reto de alterar tablas en SQLite, que carece de soporte completo para ALTER TABLE. Aprende el flujo de trabajo 'move and copy' usando las operaciones batch de Alembic.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Alembic Database Migrations, episodio 6 de 8. Estás creando una app local, probando tu migración, e intentas hacer drop de una sola columna. La base de datos da un error y descubres algo sorprendente: un simple comando drop column básicamente no existe en tu motor de base de datos. Esta es la realidad de trabajar con SQLite, y es exactamente por lo que Alembic ofrece Batch Migrations. SQLite tiene una arquitectura ligera con soporte muy limitado para modificar tablas existentes. Puedes añadir una columna a una tabla, pero si quieres hacer drop de una columna, cambiar el tipo de una columna o renombrarla, el motor de base de datos simplemente no lo soporta. Muchos developers se topan con esto cuando intentan ejecutar una operación drop column estándar en su script de Alembic. Funciona perfectamente en PostgreSQL, pero en SQLite, falla. Alembic resuelve esta limitación usando un patrón llamado el workflow de move and copy. Como la base de datos no puede modificar la estructura de la tabla in place, Alembic reconstruye toda la tabla desde cero por debajo. Para usar esta feature, no llamas a los métodos de operación estándar directamente. En su lugar, usas un context manager llamado batch alter table. Le pasas el nombre de tu tabla a este context manager, y luego defines todos tus cambios estructurales dentro de ese bloque. Cuando el bloque termina de ejecutarse, Alembic toma el control y orquesta el reemplazo de la tabla. Veamos un escenario específico. Tienes una tabla llamada user_data, y necesitas hacer drop de una columna llamada bar. Dentro de tu script, abres el context manager batch alter table para la tabla user_data. Dentro del bloque, le indicas que haga drop de la columna llamada bar. Ese es todo el código Python que escribes. En el momento en que el context manager termina, Alembic genera una secuencia de comandos SQL precisos para ejecutar el workflow de move and copy. Primero, Alembic lee la estructura actual de tu tabla. Genera un statement create table para una tabla temporal totalmente nueva. Esta nueva tabla tiene exactamente el mismo schema que la original, excepto que falta la columna bar. A continuación, Alembic copia tus datos. Ejecuta un statement insert que hace un select de todas las filas existentes de la tabla original y las mete en la nueva tabla temporal. Como la columna bar ya no existe en el nuevo schema, esos datos específicos simplemente se quedan atrás. Una vez que los datos se han copiado de forma segura, Alembic hace un drop completo de la tabla user_data original. Finalmente, renombra la tabla temporal de vuelta a user_data. La base de datos acaba exactamente en el estado que querías, y tu aplicación nunca se entera de que la tabla fue reconstruida por completo. Aquí está la clave. El context manager batch alter table agrupa tus operaciones para optimizar el rendimiento. Si necesitas hacer drop de dos columnas, añadir una nueva y cambiar un tipo de dato, pones todas esas instrucciones dentro del mismo bloque de contexto. Alembic compilará todos esos cambios y realizará el workflow de move and copy exactamente una vez. Reconstruir una tabla grande es una operación costosa que implica lecturas y escrituras pesadas en disco, por lo que hacerlo en una sola pasada es crucial. Las operaciones batch convierten una limitación severa del motor en un detalle de implementación totalmente invisible, permitiéndote escribir scripts de migración limpios y agnósticos a la base de datos, mientras Alembic se encarga del trabajo pesado de recrear la tabla de forma segura en segundo plano. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue creando!
7

Trabajando con ramas

3m 44s

Domina la colaboración en equipo manejando flujos de migración ramificados. Aprende a identificar y fusionar historiales de revisión divergentes cuando múltiples desarrolladores modifican la base de datos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de base de datos en Alembic, episodio 7 de 8. Un merge de código complicado es molesto, pero normalmente solo hace que falle un test local. Sin embargo, dos esquemas de base de datos en conflicto pueden paralizar todo tu deployment pipeline. Haces merge de tu código a main, pero de repente tu herramienta de migración de base de datos se queja de que hay multiple heads. Esta es la realidad de trabajar con branches en Alembic, y resolverlo requiere entender cómo se bifurca el timeline de tu base de datos. Los branches surgen de forma natural en cualquier entorno de equipo. El desarrollador A trabaja en una feature y genera un script de migración para añadir una tabla de carrito de la compra. Este nuevo script apunta al estado actual de la base de datos, llamémoslo revisión 100, como base. Mientras tanto, el desarrollador B trabaja en un branch diferente y genera un script para añadir una columna de cuenta. Su script también apunta a la revisión 100 como base. Ambos desarrolladores prueban en local, todo funciona bien, y ambas pull requests se mergean en el repositorio principal. Ahora tienes dos scripts de migración separados en tu proyecto, y ambos afirman ser el sucesor inmediato de la revisión 100. El timeline de la migración se ha dividido en dos caminos paralelos. Si ejecutas el comando para hacer upgrade de la base de datos a la head revision, Alembic se detendrá inmediatamente. Lanzará un error indicando que hay multiple heads. La herramienta se niega a adivinar qué migración debe aplicarse primero, ya que aplicar cambios en la base de datos en un orden impredecible es peligroso. Para resolver esto, debes reconciliar los flujos divergentes usando el comando merge de Alembic. Aquí está la clave. Hacer merge en Alembic no es como un merge de Git. No mira dentro de los archivos Python para intentar combinar automáticamente tus cambios de esquema en un solo archivo. En su lugar, el comando merge crea un script de migración completamente nuevo y vacío. Este nuevo script no contiene operaciones de base de datos. No modifica tablas ni añade columnas. Todo su propósito es estructural. Dentro del archivo Python generado, la variable down revision se establece como un tuple que contiene los IDs de revisión de ambos scripts divergentes, en lugar de un solo string. Esta única acción vuelve a unir los dos branches paralelos. Crea un nuevo head unificado para el timeline. Cuando lanzas el comando, normalmente le pasas la palabra heads, lo que le dice a Alembic que busque todos los endpoints actuales en tu historial de migración y haga un merge de ellos. También puedes adjuntar un string de mensaje para documentar la sincronización, muy parecido a un commit message. Una vez que este script de merge se genera y se hace commit en tu repositorio, tu timeline vuelve a ser lineal. La próxima vez que ejecutes el comando upgrade, Alembic ejecutará ambos parent scripts en una secuencia segura y luego marcará la base de datos con el nuevo ID de revisión del merge. La integridad estructural del historial de tu base de datos depende de esta sincronización. Un branch de Alembic es solo un fork en tu historial de migración, y arreglarlo significa generar un script dedicado que actúa como un nudo físico que vuelve a unir esos caminos divergentes en una secuencia clara. Si estos episodios te resultan útiles y quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon. Eso es todo por este episodio. Gracias por escuchar, y sigue programando.
8

Mejoras para producción

5m 00s

Sube de nivel tus conocimientos de Alembic con técnicas avanzadas. Cubrimos la invocación programática de comandos y cómo compartir una conexión con frameworks de aplicaciones como FastAPI.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Migraciones de bases de datos con Alembic, episodio 8 de 8. Para las aplicaciones modernas en contenedores, depender de scripts manuales de línea de comandos para preparar tu base de datos antes de que arranque tu aplicación es un verdadero quebradero de cabeza. Tu aplicación debería ser lo suficientemente inteligente como para verificar y actualizar su propio schema al arrancar. Para hacerlo de forma segura, necesitas Production Power-Ups. La mayoría de los desarrolladores solo conocen Alembic como una herramienta de terminal. Escribes un comando upgrade, y este modifica la base de datos. Pero la interfaz de línea de comandos es solo un thin wrapper. Por debajo está la API programática de Alembic. Puedes lanzar las migraciones directamente desde el código de tu aplicación Python. Esto permite que los frameworks de backend modernos ejecuten actualizaciones del schema automáticamente durante su rutina de inicio, asegurando que el código y la base de datos estén siempre perfectamente sincronizados. Sin embargo, hacer esto de forma ingenua introduce un problema sutil. Cuando ejecutas una migración de forma programática, Alembic utiliza su comportamiento estándar por defecto. Lee tu archivo de configuración, crea un engine de base de datos totalmente nuevo, abre una conexión, ejecuta la migración y la cierra. Pero tu aplicación acaba de arrancar. Ya ha creado un engine y ha inicializado un connection pool. Dejar que Alembic levante una conexión completamente independiente durante el arranque es ineficiente. Y lo que es más importante, puede ser peligroso. Si la secuencia de inicio de tu aplicación mantiene un lock en una tabla, la conexión independiente de Alembic se quedará colgada esperando ese lock, provocando un deadlock que hará crashear tu contenedor. Esto también es un problema grave si ejecutas tests automatizados contra una base de datos in-memory. En ese escenario, una conexión nueva apunta a una base de datos completamente vacía, lo que significa que tus migraciones no se aplicarán a los datos que realmente estás testeando. Esto lo solucionas pasándole la conexión activa de base de datos de tu aplicación directamente a Alembic. Esto se hace usando el objeto de configuración de Alembic. Primero, el código de tu aplicación instancia un objeto de configuración, apuntándolo a tu archivo de inicialización principal. Aquí está la clave. El objeto de configuración tiene un diccionario attributes. Este actúa como un puente para pasar objetos de Python activos al entorno de migración. Obtienes una conexión activa del engine de tu aplicación, y la asignas a una clave llamada connection dentro de ese diccionario attributes. A continuación, llamas a la API programática de Alembic, específicamente al comando upgrade, pasándole tu objeto de configuración modificado y diciéndole que haga el upgrade a la head revision. Pero Alembic no sabe automáticamente qué hacer con esa conexión inyectada. Tienes que modificar tu archivo de entorno de migración para completar el circuito. En la sección de tu archivo de entorno que gestiona las migraciones online, añades una simple comprobación antes de que ocurra el setup. Le indicas al script que mire dentro de los attributes de configuración. Si encuentra un objeto connection ahí, se salta la creación de un nuevo engine. En su lugar, configura el context de migración para usar la conexión que le has pasado. Si no encuentra una conexión en los attributes, hace un fallback seguro al comportamiento normal, creando un nuevo engine a partir de la URL de la base de datos. Este fallback asegura que tus herramientas de línea de comandos sigan funcionando exactamente igual que antes cuando las ejecutas en local. Diseñando tu sistema de esta manera, transformas las migraciones de una tarea externa de deploy a una parte nativa y predecible del ciclo de vida de la aplicación. Cuando se levanta una nueva instancia, pide una conexión, gestiona sus propios upgrades dentro de esa sesión, y pasa sin problemas a servir tráfico. Con esto terminamos nuestra serie sobre migraciones de bases de datos. Te animo muchísimo a que explores la documentación oficial de Alembic y pruebes estas configuraciones programáticas de forma práctica. Si tienes ideas para temas completamente nuevos que te gustaría escuchar, visita devstories dot eu y cuéntanoslo. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!