Voltar ao catálogo
Season 38 8 Episódios 30 min 2026

Alembic Database Migrations

v1.18 — Edição de 2026. Domine as migrações de bases de dados com o Alembic 1.18 em Python. Aprenda a gerir alterações de esquema, usar o autogenerate, lidar com restrições, escrever scripts offline e orquestrar migrações de bases de dados de forma eficaz em conjunto com o SQLAlchemy.

Bases de Dados Migrações de Bases de Dados ORM
Alembic Database Migrations
A Reproduzir
Click play to start
0:00
0:00
1
A Importância das Migrações
Descubra por que motivo a gestão manual de esquemas falha em grande escala e como o Alembic traz o controlo de versões para a sua base de dados relacional. Exploramos o modelo mental central das migrações de bases de dados e detalhamos a anatomia do ambiente Alembic.
3m 25s
2
Anatomia de uma Revisão
Acompanhe o ciclo de vida da sua primeira migração no Alembic. Detalhamos as funções upgrade e downgrade e revelamos como a monitorização de versões funciona realmente dentro da base de dados.
3m 46s
3
A Magia e os Limites do Autogenerate
Descubra como o Alembic deteta alterações automaticamente ao comparar os seus modelos SQLAlchemy com os metadados da base de dados em tempo real. Aprenda o que ele capta na perfeição e o que lhe escapa.
3m 26s
4
A Importância de Nomear Restrições
Descubra por que motivo depender de nomes gerados pela base de dados para as restrições é uma receita para desastres nas migrações. Aprenda a configurar uma convenção de nomenclatura unificada para o seu sistema.
3m 48s
5
Migrações Offline e Geração de SQL
Explore como gerar scripts SQL puros para os seus administradores de bases de dados em vez de executar Python diretamente na sua base de dados de produção. Discutimos o fluxo de execução offline.
4m 37s
6
Migrações em Lote para SQLite
Enfrente o desafio de alterar tabelas no SQLite, que não tem suporte completo para ALTER TABLE. Aprenda o fluxo de trabalho 'move and copy' usando as operações em lote do Alembic.
3m 33s
7
Trabalhar com Branches
Domine a colaboração em equipa lidando com fluxos de migração com branches. Aprenda a identificar e a fazer merge de históricos de revisão divergentes quando vários programadores modificam a base de dados.
3m 21s
8
Otimizações para Produção
Eleve o seu conhecimento de Alembic com técnicas avançadas. Abordamos a invocação programática de comandos e a partilha de uma ligação com frameworks de aplicações como o FastAPI.
4m 16s

Episódios

1

A Importância das Migrações

3m 25s

Descubra por que motivo a gestão manual de esquemas falha em grande escala e como o Alembic traz o controlo de versões para a sua base de dados relacional. Exploramos o modelo mental central das migrações de bases de dados e detalhamos a anatomia do ambiente Alembic.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Database Migrations com Alembic, episódio 1 de 8. Corres um comando manual na base de dados diretamente no teu servidor de produção para adicionar uma única coluna, e de repente a aplicação bloqueia. O teu código em deploy esperava uma estrutura, a base de dados agora tem outra, e não há um botão simples de undo. É por isso que estamos a analisar a importância das migrations. Quando um projeto de software começa, alterar o schema da base de dados é fácil. Fazes drop das tabelas e voltas a criá-las. Assim que tens utilizadores reais e dados reais, isso deixa de ser opção. Fazer alterações manuais em diferentes ambientes, como development, staging e produção, acaba por levar a um mismatch. O código da tua aplicação depende de um state específico da base de dados. Quando esse state diverge, a aplicação falha. O Alembic resolve isto atuando como version control para o schema da tua base de dados. Tal como fazes o tracking do histórico do teu source code, o Alembic faz o tracking do histórico da estrutura da tua base de dados. Para usares o Alembic, inicializas um environment de migrations. Esta é uma estrutura de diretórios dedicada que fazes commit para o teu repositório de source control juntamente com o código da tua aplicação. Contém as instruções e a configuração necessárias para modificar a tua base de dados ao longo do tempo. O environment consiste em três partes principais. A primeira é o ficheiro de configuração root, chamado alembic ponto ini. Este ficheiro fica na base do teu projeto. Guarda as settings básicas, principalmente o URL de conexão à base de dados, dizendo ao Alembic onde a base de dados de destino está realmente. A seguir, temos a pasta versions. É aqui que os scripts de migrations são guardados. Sempre que precisas de alterar o schema da base de dados, um novo script Python é criado nesta pasta. Cada script define duas ações: uma função de upgrade para aplicar a alteração, e uma função de downgrade para a reverter. Se precisares de adicionar uma tabela para perfis de utilizador, a instrução exata para essa alteração vive num script mesmo aqui. A última peça é um ficheiro chamado env ponto py. É fácil confundir isto com um ficheiro de configuração geral da aplicação ou um local para guardar variáveis de sistema, mas não é esse o seu propósito. Aqui está o ponto chave. O ficheiro env ponto py serve especificamente para fazer a ponte entre os teus models da aplicação e o engine de migrations do Alembic. Ele configura o engine da base de dados, gere o lifecycle da conexão e, mais importante, carrega a tua metadata do SQLAlchemy. Isto diz ao Alembic exatamente como os teus models são no código, para que ele saiba a que schema da base de dados deve eventualmente corresponder. Sempre que invocas um comando do Alembic, ele corre primeiro este script env ponto py para estabelecer o contexto de que precisa para operar. Em vez de dependeres de uma memória frágil de comandos manuais na base de dados, tens um processo estruturado e repetível. O verdadeiro valor do environment do Alembic não é apenas correr comandos com segurança, mas sim criar um histórico definitivo e versionado de como exatamente as tuas estruturas de dados evoluíram desde o primeiro dia. Se gostas do podcast e queres apoiar o programa, podes encontrar-nos pesquisando por DevStoriesEU no Patreon. Gostaria de tirar um momento para te agradecer por ouvires — ajuda-nos imenso. Tem um ótimo dia!
2

Anatomia de uma Revisão

3m 46s

Acompanhe o ciclo de vida da sua primeira migração no Alembic. Detalhamos as funções upgrade e downgrade e revelamos como a monitorização de versões funciona realmente dentro da base de dados.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Alembic Database Migrations, episódio 2 de 8. Já te perguntaste como é que uma database sabe realmente qual é a versão do schema que está a correr neste momento? Não está a inspecionar as tuas tabelas nem a adivinhar com base nas colunas que existem. O segredo está numa única tabela oculta, e perceber como ela se liga ao teu código é o núcleo da Anatomia de uma Revision. Para alterar o schema de uma database, primeiro crias um migration script. Fazes isso ao correr o comando alembic revision junto com uma mensagem descritiva curta, como create account table. O Alembic gera um novo ficheiro Python na tua pasta versions. O nome do ficheiro começa com uma string aleatória de caracteres, seguida pela tua mensagem. Essa string é um GUID parcial, um identificador globalmente único. O Alembic usa estes identificadores em vez de números inteiros sequenciais para evitar merge conflicts quando vários developers estão a criar migrations em branches diferentes ao mesmo tempo. Se abrires esse novo ficheiro Python, vais ver duas variáveis perto do topo: revision e down revision. A variável revision guarda o GUID para este script específico. A variável down revision guarda o GUID do script que veio logo antes dele. Aqui está o ponto chave. Os developers muitas vezes pensam que as migrations são aplicadas por ordem, com base nos timestamps de criação dos ficheiros ou na ordem alfabética dos nomes dos ficheiros. Mas não são. O Alembic depende estritamente da down revision chain. Ele lê estas variáveis dentro dos ficheiros para construir uma linked list do histórico do teu schema. Se um script não apontar para uma revision anterior válida, a chain quebra. Abaixo destas routing variables, vais encontrar duas funções vazias: upgrade e downgrade. É aqui que escreves manualmente as alterações do teu schema. Para o nosso cenário, estamos a adicionar uma account table. Na função upgrade, escreves a lógica para criar a tabela, definindo as tuas colunas, como uma primary key integer e uma string para o nome da conta. A função downgrade deve fazer exatamente o oposto. Se o upgrade cria a account table, o downgrade deve fazer drop dela. Cada passo para a frente deve ter um passo para trás correspondente e fiável. Depois de escreveres o script, aplicas o mesmo ao correr o comando alembic upgrade, apontando para head, que significa a revision mais recente na tua chain. Aqui está o que acontece nos bastidores. O Alembic liga-se à tua database e procura por uma tabela chamada alembic version. Se esta for a tua primeira migration, a tabela ainda não existe, então o Alembic cria-a. Esta tabela tem exatamente uma linha e uma coluna, a guardar o GUID da revision atualmente aplicada. O Alembic verifica esta tabela, vê onde a database está neste momento, e corre as funções de upgrade de cada script necessário para alcançar a target revision. Finalmente, atualiza a version table com o teu novo GUID. Se testares a tua nova account feature e perceberes que algo está errado, podes fazer rollback de forma limpa. Corres o comando alembic downgrade, passando um identificador relativo como menos um para recuar uma única revision. O Alembic olha para a versão atual na database, encontra o script correspondente, e corre a sua função de downgrade. Faz drop da account table e atualiza a version table com o GUID anterior. A principal conclusão é que um migration script não é apenas uma coleção solta de comandos de database. É um node independente numa linked list que dá à tua database um caminho preciso para se mover tanto para a frente como para trás no tempo. Obrigado por passares uns minutos comigo. Até à próxima, fica bem.
3

A Magia e os Limites do Autogenerate

3m 26s

Descubra como o Alembic deteta alterações automaticamente ao comparar os seus modelos SQLAlchemy com os metadados da base de dados em tempo real. Aprenda o que ele capta na perfeição e o que lhe escapa.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Database Migrations com Alembic, episódio 3 de 8. Confiares cegamente numa ferramenta automatizada para escrever as tuas database migrations é meio caminho andado para apagares acidentalmente as tuas tabelas de produção. O problema está em perceberes o que a ferramenta realmente vê quando compara o teu código com a realidade, e é exatamente isso que vamos cobrir hoje em A Magia e os Limites do Autogenerate. O Alembic tem uma feature principal chamada autogenerate. Quando corres um comando de revision com a flag autogenerate, o Alembic faz uma comparação. Primeiro, liga-se à tua base de dados live e inspeciona o schema atual. Segundo, olha para o estado de destino definido pelos models SQLAlchemy no código da tua aplicação. Compara estes dois estados e descobre as diferenças. Há um equívoco comum sobre este passo. O autogenerate não aplica magicamente estas alterações à tua base de dados. Simplesmente escreve um draft de um script Python com as operações de migration que acha que precisas para fazer a base de dados corresponder aos teus models. Tens de rever este script candidato antes de o executares na tua base de dados. Quando compara a tua base de dados com os teus models, o autogenerate deteta de forma fiável alterações estruturais básicas. Se adicionares uma nova model class ao teu código, o Alembic escreve uma instrução para criar uma tabela. Se removeres uma model class, escreve uma instrução para fazer drop dessa tabela. Apanha corretamente quando adicionas ou removes colunas, quando alteras uma coluna para permitir valores null, ou quando adicionas indexes básicos e unique constraints. Para estas operações standard, a feature poupa-te uma quantidade enorme de digitação manual. Aqui está a parte que interessa. O autogenerate tem pontos cegos porque não consegue ler a tua mente. Supõe que decides renomear uma tabela existente nos teus models SQLAlchemy. Atualizas o código e corres o comando autogenerate, à espera que o Alembic escreva um comando seguro para alterar o nome da tabela. Em vez disso, propõe algo altamente perigoso. Escreve um comando para fazer drop da tabela antiga por completo, destruindo todos os dados lá dentro, e depois escreve um segundo comando para criar uma tabela nova em folha com o novo nome. O Alembic faz isto porque apenas vê que o nome da tabela antiga existe na base de dados, mas falta nos teus models, e que um novo nome de tabela existe nos teus models, mas falta na base de dados. Não tem forma de ligar as duas coisas como um simples rename. Tens de editar manualmente o script gerado para usares uma operação de rename table em vez disso. Exatamente a mesma limitação aplica-se a renomear colunas. O autogenerate vai interpretar uma coluna renomeada como uma instrução para fazer drop da antiga e adicionar uma nova. Para além de renames, há alterações que o autogenerate vai ignorar por completo by default. Se alterares o data type de uma coluna, ou se modificares um valor de server default, o Alembic vai ignorar essas diferenças. Podes configurar a ferramenta para detetar alterações de type e default, mas tens de ativar essas settings explicitamente na tua environment configuration. Mesmo com essas settings ativadas, nunca vai detetar alterações a sequence objects ou constraint names. A forma mais segura de tratar o autogenerate é como uma ferramenta de ditado de alta velocidade que trata do boilerplate por ti, em vez de um sistema inteligente que percebe a intenção por trás das tuas alterações de código. Obrigado por estares aí. Espero que tenhas aprendido algo novo.
4

A Importância de Nomear Restrições

3m 48s

Descubra por que motivo depender de nomes gerados pela base de dados para as restrições é uma receita para desastres nas migrações. Aprenda a configurar uma convenção de nomenclatura unificada para o seu sistema.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Migrações de base de dados com Alembic, episódio 4 de 8. A maneira mais fácil de arruinar um deploy é tentares fazer drop a uma constraint da base de dados à qual nunca chegaste a dar um nome. O teu script de migration funciona perfeitamente na tua máquina local, mas o ambiente de staging acabou de ir abaixo, a queixar-se de uma foreign key em falta com um nome imprevisível como SYS C 0 0 2 9 3 3 4. O culpado é confiares em identificadores gerados pela base de dados. Este episódio aborda a importância de dar nome às constraints e como as automatizar. Muitos developers assumem que o object-relational mapper lida com o drop de colunas e as suas regras associadas de forma transparente. Definem uma unique constraint ou uma foreign key no seu model, omitem o nome para poupar tempo e seguem em frente. Quando fazes isto, o motor da base de dados assume o controlo. Sistemas como o Postgres ou o Oracle vão atribuir automaticamente um nome arbitrário, gerado pelo sistema, para aplicar essa regra. Isto cria uma bomba-relógio para futuras migrations. Quando eventualmente precisares de alterar ou fazer drop a essa tabela ou coluna, o Alembic usa a operação drop constraint. Essa operação exige estritamente o nome exato da constraint de destino. Se deixares a base de dados gerar o nome, ele quase de certeza que vai ser diferente em development do que em staging ou em production. Acabas por fazer hardcode de um nome de constraint local no teu script de migration, o que falha imediatamente quando é corrido num ambiente diferente onde essa string aleatória não existe. Para corrigir isto, cada constraint na tua base de dados tem de ter um nome explícito e determinístico. Fazer isto manualmente em centenas de models é chato e fácil de esquecer. A melhor abordagem é configurar um dicionário de naming conventions no teu objeto MetaData do SQLAlchemy. Este dicionário serve como um template global para a tua aplicação. Defines regras para cada tipo de constraint. Por exemplo, podes especificar que cada index deve ser nomeado usando o prefixo i x, seguido pelo nome da tabela e, depois, pelo nome da coluna. Defines templates semelhantes para unique constraints, check constraints e foreign keys. A seguir, anexas este objeto MetaData configurado à tua declarative base class. Aqui está a parte que interessa. Assim que este dicionário estiver configurado, o Alembic integra automaticamente as tuas naming conventions tanto na sua feature de autogenerate como nas suas operações manuais. Quando corres um comando para fazer autogenerate de uma nova migration, o Alembic olha para os teus models, vê uma nova constraint e verifica o dicionário MetaData. Ele aplica o teu template, calcula o nome explícito e escreve essa string exata no script Python gerado. Como o script gerado dá a instrução explícita à base de dados para usar esse nome específico, a constraint vai ser idêntica em todos os ambientes. Esta integração estende-se às operações do Alembic executadas durante o próprio processo de upgrade. Se um script de migration incluir uma operação create table ou add column com inline constraints que não tenham nomes explícitos, o Alembic não as passa simplesmente às cegas para a base de dados. Ele interceta-as, consulta o template de naming convention e atribui o nome determinístico correto antes de executar os comandos na base de dados. Uma naming convention determinística garante que uma regra criada na tua máquina local vai partilhar exatamente o mesmo identificador quando chegar aos teus servidores de production, eliminando por completo o risco de constraints às quais não consegues fazer drop. Obrigado por ouvirem. Fiquem bem, pessoal.
5

Migrações Offline e Geração de SQL

4m 37s

Explore como gerar scripts SQL puros para os seus administradores de bases de dados em vez de executar Python diretamente na sua base de dados de produção. Discutimos o fluxo de execução offline.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Alembic Database Migrations, episódio 5 de 8. Em ambientes corporativos altamente regulamentados, os developers quase nunca têm acesso direto para correr Python em bases de dados de produção. Quando chega o dia do deploy, os administradores de bases de dados geralmente bloqueiam-te o acesso e exigem antes um script raw que possa ser revisto. Fazer a ponte entre a tua codebase em Python e o rigoroso processo de deploy deles é algo tratado pelas migrações offline e pela geração de SQL. Normalmente, o Alembic liga-se a uma base de dados live e executa as alterações de schema diretamente através dessa ligação. Mas quando precisas de entregar um ficheiro de texto simples a uma equipa de DBA, usas o modo offline. Ao acrescentares a flag traço traço sql aos teus comandos de upgrade ou downgrade no terminal, o Alembic altera completamente o seu comportamento de execução. Em vez de correr as statements contra um database engine, renderiza-as como uma string contínua de SQL standard e faz o dump diretamente para o standard output. Podes facilmente redirecionar este output do terminal para um ficheiro de texto. Este comportamento duplo não é magia, é explicitamente definido no ficheiro de environment do teu projeto, tipicamente chamado env ponto py. Se olhares para dentro desse ficheiro, vais encontrar duas funções de routing distintas. A primeira é a run migrations online. Esta função cria um database engine live, faz o bind de uma ligação ativa ao context do Alembic, e corre os teus scripts de migração passo a passo. A segunda função é a run migrations offline, e é aqui que a tradução acontece. Quando passas a flag sql, o Alembic deteta a flag e, em vez disso, chama esta função offline. Ela configura o context usando apenas o URL da base de dados. Nenhuma ligação de rede é feita, e nenhum engine é instanciado. Depois, pega nas tuas estruturas de migração em Python e gera as statements exatas de CREATE, ALTER ou DROP, envolve-as em blocos de transação standard BEGIN e COMMIT, e formata tudo para o dialeto específico da tua base de dados. Aqui está o ponto chave. Como o modo offline nunca se liga realmente à base de dados, os teus scripts de migração não podem depender do estado ativo da base de dados. Não podes executar uma statement SELECT dentro de uma migração offline para verificar se uma row existe, e não podes inspecionar o estado atual de uma tabela antes de fazeres uma alteração. Se o teu código Python espera que um cursor da base de dados devolva dados para decidir que alteração de schema fazer, a geração offline vai falhar. O script tem de ser puramente declarativo. Simplesmente diz ao Alembic que estruturas gerar. Considera um developer a terminar uma feature branch local. Ele correu as migrações localmente em modo online para verificar se tudo funciona contra a sua base de dados de testes. Para a release de produção, ele corre o comando de upgrade com uma start revision e end revision específicas, adiciona a flag sql, e encaminha o output para um ficheiro de texto. O resultado é um script SQL limpo e sequencial. O developer entrega este ficheiro à equipa de DBA. Os DBAs podem lê-lo, verificá-lo contra as suas rigorosas políticas de segurança, e aplicá-lo durante a janela de manutenção usando ferramentas standard de administração de bases de dados. Também tens controlo sobre como este output offline é gerado. Dentro da função run migrations offline, a chamada context configure aceita parâmetros que ajustam o SQL renderizado. Um requisito comum é converter variáveis em valores literais. Ao ativares os literal binds na configuração, garantes que quaisquer dados inseridos durante a migração incluem os valores reais diretamente na string SQL, em vez de fazer o output de marcadores de parâmetros genéricos. Isto garante que o output seja um script totalmente independente, pronto para execução. O verdadeiro valor da geração offline é a previsibilidade; transforma mudanças dinâmicas de estado do Python em SQL estático e auditável, que qualquer pipeline de deploy ou equipa de segurança pode verificar antes que uma única tabela seja modificada. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
6

Migrações em Lote para SQLite

3m 33s

Enfrente o desafio de alterar tabelas no SQLite, que não tem suporte completo para ALTER TABLE. Aprenda o fluxo de trabalho 'move and copy' usando as operações em lote do Alembic.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. Migrações de base de dados do Alembic, episódio 6 de 8. Estás a construir uma app local, a testar a tua migration, e tentas fazer drop a uma única coluna. A base de dados atira um erro, e descobres um facto surpreendente: um simples comando de drop column fundamentalmente não existe no teu database engine. Esta é a realidade de trabalhar com SQLite, e é exatamente por isso que o Alembic oferece Batch Migrations. O SQLite tem uma arquitetura leve com suporte muito limitado para alterar tabelas existentes. Podes adicionar uma coluna a uma tabela, mas se quiseres fazer drop de uma coluna, mudar o tipo de uma coluna, ou renomear uma coluna, o database engine simplesmente não suporta isso. Muitos developers deparam-se com isto quando tentam correr uma operação standard de drop column no seu script de Alembic. Funciona perfeitamente em PostgreSQL, mas em SQLite, crasha. O Alembic resolve esta limitação usando um pattern chamado workflow de move and copy. Como a base de dados não consegue modificar a estrutura da tabela in place, o Alembic reconstrói a tabela inteira do zero nos bastidores. Para usares esta feature, não chamas os métodos de operação standard diretamente. Em vez disso, usas um context manager chamado batch alter table. Passas o nome da tua tabela para este context manager, e depois defines todas as tuas alterações estruturais dentro desse bloco. Quando o bloco termina a execução, o Alembic assume o controlo e orquestra a substituição da tabela. Vamos olhar para um cenário específico. Tens uma tabela chamada user_data, e precisas de fazer drop a uma coluna chamada bar. Dentro do teu script, abres o context manager batch alter table para a tabela user_data. Dentro do bloco, dás a instrução para fazer drop da coluna chamada bar. Esse é todo o código Python que escreves. No momento em que o context manager termina, o Alembic gera uma sequência de comandos SQL precisos para executar o workflow de move and copy. Primeiro, o Alembic lê a estrutura atual da tua tabela. Ele gera um statement de create table para uma tabela temporária totalmente nova. Esta nova tabela tem exatamente o mesmo schema da original, exceto que a coluna bar está em falta. A seguir, o Alembic copia os teus dados. Ele corre um statement de insert que faz select a todas as rows existentes da tabela original e empurra-as para a nova tabela temporária. Como a coluna bar já não existe no novo schema, esses dados específicos são simplesmente deixados para trás. Assim que os dados são copiados com segurança, o Alembic faz drop por completo da tabela user_data original. Por fim, renomeia a tabela temporária de volta para user_data. A base de dados acaba exatamente no estado que querias, e a tua app nunca sabe que a tabela foi completamente reconstruída. Aqui está o insight principal. O context manager batch alter table agrupa as tuas operações em batch para melhor performance. Se precisares de fazer drop a duas colunas, adicionar uma nova, e mudar um data type, pões todas essas instruções dentro do mesmo bloco de contexto. O Alembic vai compilar todas essas alterações e executar o workflow de move and copy exatamente uma vez. Reconstruir uma tabela grande é uma operação pesada que envolve muitas reads e writes no disco, por isso fazê-lo numa única passagem é crucial. As operações em batch transformam uma limitação grave do engine num detalhe de implementação completamente invisível, permitindo-te escrever scripts de migration limpos e database-agnostic, enquanto o Alembic trata do trabalho pesado da recriação de tabelas com segurança em background. É tudo por este episódio. Obrigado por ouvires, e continua a construir!
7

Trabalhar com Branches

3m 21s

Domine a colaboração em equipa lidando com fluxos de migração com branches. Aprenda a identificar e a fazer merge de históricos de revisão divergentes quando vários programadores modificam a base de dados.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. Migrações de base de dados no Alembic, episódio 7 de 8. Um merge de código confuso é chato, mas normalmente só falha num teste local. Dois schemas de base de dados em conflito, por outro lado, podem paralisar todo o teu pipeline de deploy. Fazes o merge do teu código para a main, mas de repente a tua ferramenta de migração de base de dados queixa-se de múltiplos heads. Esta é a realidade de trabalhar com branches no Alembic, e resolver isto exige perceber como é que a timeline da tua base de dados faz um fork. As branches surgem naturalmente em qualquer ambiente de equipa. O developer A trabalha numa feature e gera um script de migração para adicionar uma tabela de carrinho de compras. Este novo script aponta para o estado atual da base de dados, vamos chamar-lhe revisão 100, como a sua base. Entretanto, o developer B trabalha numa branch diferente e gera um script para adicionar uma coluna de conta. O script dele também aponta para a revisão 100 como base. Ambos os developers testam localmente, tudo funciona bem, e é feito o merge de ambos os pull requests para o repositório principal. Agora tens dois scripts de migração separados no teu projeto, ambos a alegar serem o sucessor imediato da revisão 100. A timeline de migração dividiu-se em dois caminhos paralelos. Se correres o comando para fazer upgrade da base de dados para a head revision, o Alembic vai parar imediatamente. Ele vai lançar um erro a dizer que existem múltiplos heads. A ferramenta recusa-se a adivinhar qual a migração que deve ser aplicada primeiro, porque aplicar alterações na base de dados numa ordem imprevisível é perigoso. Para resolver isto, tens de reconciliar as streams divergentes usando o comando merge do Alembic. Aqui está o ponto chave. Fazer um merge no Alembic não é como um merge no Git. Ele não olha para dentro dos ficheiros Python para tentar combinar automaticamente as alterações do teu schema num único ficheiro. Em vez disso, o comando merge cria um script de migração completamente novo e vazio. Este novo script não contém operações de base de dados. Não altera tabelas nem adiciona colunas. O seu único propósito é estrutural. Dentro do ficheiro Python gerado, a variável down revision é definida como um tuple que contém os IDs de revisão de ambos os scripts divergentes, em vez de uma única string. Esta única ação volta a unir as duas branches paralelas. Cria um novo head unificado para a timeline. Quando corres o comando, normalmente passas-lhe a palavra heads, que diz ao Alembic para encontrar todos os endpoints atuais no teu histórico de migrações e fazer o merge deles. Também podes anexar uma string de mensagem para documentar a sincronização, muito parecido com uma commit message. Assim que este script de merge for gerado e feito o commit para o teu repositório, a tua timeline volta a ser linear. Da próxima vez que correres o comando de upgrade, o Alembic vai executar ambos os parent scripts numa sequência segura e, em seguida, vai marcar a base de dados com o novo ID de revisão do merge. A integridade estrutural do teu histórico da base de dados depende desta sincronização. Uma branch no Alembic é apenas um fork no teu histórico de migrações, e corrigi-la significa gerar um script dedicado que atua como um nó físico, voltando a unir esses caminhos divergentes numa sequência clara. Se achas estes episódios úteis e queres apoiar o programa, podes procurar por DevStoriesEU no Patreon. É tudo por este episódio. Obrigado por ouvires, e continua a programar!
8

Otimizações para Produção

4m 16s

Eleve o seu conhecimento de Alembic com técnicas avançadas. Abordamos a invocação programática de comandos e a partilha de uma ligação com frameworks de aplicações como o FastAPI.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Migrações de base de dados Alembic, episódio 8 de 8. Para aplicações containerized modernas, depender de scripts manuais de command-line para preparar a tua base de dados antes da tua app arrancar é uma dor de cabeça operacional. A tua aplicação deve ser inteligente o suficiente para verificar e atualizar o seu próprio schema no startup. Para fazeres isso com segurança, precisas de Production Power-Ups. A maioria dos developers conhece o Alembic apenas como uma ferramenta de terminal. Escreves um comando de upgrade, e ele modifica a base de dados. Mas a command-line interface é apenas um thin wrapper. Por baixo, está a API programática do Alembic. Podes disparar migrações diretamente a partir do código Python da tua aplicação. Isto permite que frameworks de backend modernos executem updates de schema automaticamente durante a sua rotina de startup, garantindo que o código e a base de dados estão sempre perfeitamente sincronizados. No entanto, fazer isto de forma ingénua introduz um problema subtil. Quando executas uma migração programaticamente, o Alembic adota o seu comportamento por defeito. Lê o teu ficheiro de configuração, cria um database engine completamente novo, abre uma connection, corre a migração e fecha-a. Mas a tua aplicação acabou de arrancar. Já criou um engine e inicializou uma connection pool. Deixar o Alembic criar uma connection totalmente separada durante o startup é ineficiente. Mais importante ainda, pode ser perigoso. Se a sequência de startup da tua aplicação tiver um lock numa tabela, a connection separada do Alembic vai ficar pendurada à espera desse lock, causando um deadlock que crasha o teu container. Isto também é um grande problema se correres testes automatizados numa base de dados in-memory. Nesse cenário, uma connection totalmente nova aponta para uma base de dados completamente vazia, o que significa que as tuas migrações vão falhar ao serem aplicadas aos dados que estás realmente a testar. Resolves isto passando a connection de base de dados ativa da tua aplicação diretamente para o Alembic. Isto é feito usando o objeto de configuração do Alembic. Primeiro, o código da tua aplicação instancia um objeto de configuração, apontando-o para o teu ficheiro de inicialização principal. Aqui está o ponto chave. O objeto de configuração tem um dicionário de atributos. Isto atua como uma ponte para passar objetos Python live para o ambiente de migração. Obténs uma connection ativa do engine da tua aplicação, e atribuis essa connection a uma key chamada connection dentro desse dicionário de atributos. A seguir, chamas a API programática do Alembic, especificamente o comando de upgrade, passando-lhe o teu objeto de configuração modificado e dizendo-lhe para fazer upgrade para a head revision. Mas o Alembic não sabe automaticamente o que fazer com essa connection injetada. Tens de modificar o teu ficheiro de ambiente de migração para completar o circuito. Na secção do teu ficheiro de ambiente que lida com migrações online, adicionas uma verificação simples antes de o setup acontecer. Instruis o script a procurar dentro dos atributos de configuração. Se encontrar lá um objeto connection, ignora a criação de um novo engine. Em vez disso, configura o contexto de migração para usar a connection que forneceste. Se não encontrar uma connection nos atributos, faz um fallback seguro para o comportamento normal, criando um novo engine a partir do URL da base de dados. Este fallback garante que as tuas ferramentas de command-line continuam a funcionar exatamente como antes quando as corres localmente. Ao desenhares o teu sistema desta forma, transformas as migrações de uma tarefa externa de deploy numa parte nativa e previsível do ciclo de vida da aplicação. Quando uma nova instância arranca, pede uma connection, gere os seus próprios upgrades dentro dessa sessão, e transita de forma fluida para servir tráfego. Isto conclui a nossa série sobre migrações de bases de dados. Recomendo vivamente que explores a documentação oficial do Alembic e experimentes estas configurações programáticas na prática. Se tiveres ideias para tópicos inteiramente novos que gostarias de ouvir, visita devstories dot eu e avisa-nos. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!