Pesquisa de site

Como realizar migrações Flask-SQLAlchemy usando Flask-Migrate


O autor selecionou a Apache Software Foundation para receber uma doação como parte do programa Write for DOnations.

Introdução

Flask é uma estrutura web Python leve que fornece ferramentas e recursos valiosos para a criação de aplicativos web na linguagem Python. SQLAlchemy é um kit de ferramentas SQL que oferece acesso a banco de dados relacional eficiente e de alto desempenho. Ele fornece maneiras de interagir com diversos mecanismos de banco de dados, como SQLite, MySQL e PostgreSQL. Dá acesso às funcionalidades SQL do banco de dados. Ele também fornece um Mapeador Relacional de Objetos (ORM), que permite fazer consultas e manipular dados usando objetos e métodos Python simples. Flask-SQLAlchemy é uma extensão Flask que facilita o uso do SQLAlchemy com Flask, fornecendo ferramentas e técnicas para interagir com seu banco de dados em seus aplicativos Flask por meio do SQLAlchemy.

Flask-Migrate é uma extensão Flask baseada na biblioteca Alembic, permitindo gerenciar migrações de banco de dados.

A migração de banco de dados é a transferência de dados entre diferentes esquemas de banco de dados sem qualquer perda de dados. É comumente usado para atualizar um banco de dados, alterar seu esquema adicionando novas colunas de tabela ou relacionamentos e garantir uma transição tranquila com tempo de inatividade mínimo e sem perda de dados.

Por exemplo, se você tiver uma tabela chamada Produto com uma lista de nomes de produtos e quiser adicionar uma coluna de preço a essa tabela, poderá usar a migração do banco de dados para adicionar a coluna de preço sem perder os dados existentes do produto. .

Neste tutorial, você usará Flask-Migrate com Flask-SQLAlchemy para realizar migrações de esquema de banco de dados para modificar suas tabelas e preservar dados.

Pré-requisitos

  • Para um ambiente de programação local Python 3, siga o tutorial para sua distribuição na série Como instalar e configurar um ambiente de programação local para Python 3. Neste tutorial, chamaremos nosso diretório de projeto de flask_app.

  • Uma compreensão dos conceitos básicos do Flask, como rotas, funções de visualização e modelos. Se você não estiver familiarizado com o Flask, consulte Como criar seu primeiro aplicativo da Web usando Flask e Python e Como usar modelos em um aplicativo Flask.

  • Uma compreensão dos conceitos básicos de HTML. Revise nossa série de tutoriais Como construir um site com HTML.

  • Entenda os conceitos básicos do Flask-SQLAlchemy, como configurar um banco de dados, criar modelos de banco de dados e inserir dados no banco de dados. Consulte Como usar o Flask-SQLAlchemy para interagir com bancos de dados em um aplicativo Flask para obter conhecimento prévio.

Etapa 01 - Instalando os pacotes Python necessários

Nesta etapa, você instalará os pacotes necessários para seu aplicativo.

No diretório flask_app, ative seu ambiente virtual:

source <my_env>/bin/activate

Com seu ambiente virtual ativado, use pip para instalar Flask, Flask-SQLAlchemy e Flask-Migrate:

pip install Flask Flask-SQLAlchemy Flask-Migrate

Assim que a instalação for concluída, a saída imprimirá uma linha semelhante a esta:

Successfully installed Flask-3.0.0 Flask-Migrate-4.0.5 Flask-SQLAlchemy-3.1.1 Jinja2-3.1.2 Mako-1.3.0 MarkupSafe-2.1.3 Werkzeug-3.0.1 alembic-1.12.1 blinker-1.7.0 click-8.1.7 greenlet-3.0.1 itsdangerous-2.1.2 sqlalchemy-2.0.23 typing-extensions-4.8.0

Com os pacotes Python necessários instalados, você configurará um banco de dados e um modelo de exemplo a seguir.

Passo 02 - Configurando um banco de dados e modelo de exemplo

Nesta etapa, você configurará seu aplicativo Flask e um banco de dados Flask-SQLAlchemy com um modelo representando uma tabela de produtos onde você armazenará os produtos de sua loja.

No diretório flask_app, abra um novo arquivo chamado app.py. Ele conterá o código principal do seu aplicativo Flask:

nano app.py

Adicione o seguinte código a ele:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
db = SQLAlchemy(app)


class Product(db.Model):
    __tablename__ = "products"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    def __repr__(self):
        return f"<Product {self.name}>"
@app.route("/")
def index():
    return "<h1>Hello, World!</h1>"

Salve e feche o arquivo.

Você importa a classe Flask e SQLAlchemy dos módulos flask e flask_sqlalchemy aqui. Em seguida, você cria uma instância do aplicativo Flask chamada app.

Em seguida, você define o URI do banco de dados SQLite na configuração do aplicativo; isso especifica o nome do arquivo do banco de dados como app.db. Este arquivo de banco de dados será criado em uma nova pasta chamada instance, que será gerada pelo Flask no diretório principal flask_app.

Em db=SQLAlchemy(app), você cria uma instância SQLAlchemy, db, ignorando o aplicativo Flask como argumento.

A seguir, você cria uma classe Product que herda de db.Model, representando uma tabela de banco de dados chamada ‘products’. Na tabela, você define uma coluna id para o ID do produto e uma coluna name para o nome do produto.

A função especial repr permite que você forneça a cada objeto uma representação de string para reconhecê-lo para fins de depuração.

Por fim, você cria uma rota ('/') que retorna uma resposta HTML simples ("

Hello, World!

") quando o URL raiz é acessado.

Execute o seguinte comando para testar se o aplicativo está configurado corretamente. Isso executa o aplicativo Flask app em um servidor de desenvolvimento com depuração ativada:

flask --app app run --debug

Depois de executar este comando, você receberá a seguinte saída:

 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Please do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 633-501-683

Isso fornece informações sobre seu aplicativo enquanto ele é executado.

Com o servidor de desenvolvimento em execução, visite o seguinte URL usando seu navegador:

http://127.0.0.1:5000/

Você receberá um cabeçalho <h1>, ‘Hello, World!`. Isso confirma que o aplicativo está configurado corretamente. Agora você pode passar para a próxima etapa e adicionar Flask-Migrate ao seu aplicativo.

Passo 03 - Adicionando Flask-Migrate à sua aplicação

Nesta etapa, você modificará o arquivo do aplicativo app.py para adicionar o Flask-Migrate e usá-lo para gerenciar seu banco de dados Flask-SQLAlchemy.

Primeiro, abra app.py para modificação:

nano app.py

Modifique o arquivo para que tudo acima da declaração da classe Product fique como segue:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class Product(db.Model):

Salve e feche o arquivo.

Você importa a classe Migrate do pacote flask_migrate.

Após a declaração db, você usa a classe Migrate para iniciar uma instância de migração chamada migrate, passando-a para o app Flask. e a instância do banco de dados db.

Flask-Migrate fornece um auxiliar de comando flask db para gerenciar seu banco de dados.

Para concluir a configuração do Flask-Migrate e adicionar suporte ao seu projeto atual, use o seguinte comando no diretório flask_app:

flask db init

Você receberá uma saída semelhante a esta:

[secondary_label Output]  
Creating directory 'flask_app/migrations' ...  done
  Creating directory 'flask_app/migrations/versions' ...  done
  Generating flask_app/migrations/README ...  done
  Generating flask_app/migrations/script.py.mako ...  done.
  Generating flask_app/migrations/env.py ...  done
  Generating flask_app/migrations/alembic.ini ...  done
  Please edit the configuration/connection/logging settings in
  'flask_app/migrations/alembic.ini' before proceeding.

Isso cria um novo diretório migrations dentro da sua pasta flask_app, onde todos os scripts de migração que gerenciam suas migrações serão armazenados.

Se você conhece o Alembic e deseja adicionar configurações avançadas ao seu sistema de migração de banco de dados, você pode modificar o arquivo migrations/alembic.ini gerado. Para nossos propósitos, deixaremos como está.

Observação: o diretório migrations contém arquivos que gerenciam as migrações de banco de dados do seu aplicativo e eles devem ser adicionados ao seu repositório de controle de versão com o restante do código do seu aplicativo.

Com o Flask-Migrate conectado à sua aplicação, você realizará uma migração inicial do banco de dados. Usar a classe 'Produto' criará a tabela de produtos que você declarou anteriormente.

Criando a tabela de produtos usando um script de migração

Agora você realizará sua primeira migração, criando a tabela Produtos do seu banco de dados.

No diretório flask_app, execute o seguinte comando. Este comando flask db migrate detecta todas as novas tabelas ou modificações que você executa em seus modelos de banco de dados Flask-SQLAlchemy. O sinalizador -m permite especificar uma mensagem curta descrevendo a modificação realizada:

flask db migrate -m "initial migration"

Você receberá a seguinte saída:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'products'
  Generating
  flask_app/migrations/versions/b9198aca3963_initial_migration.py
  ...done

Como nosso banco de dados ainda não foi criado, esta saída informa que uma nova tabela chamada produtos foi detectada e um script de migração chamado b9198aca3963 inicial_migration.py foi criado dentro de um diretório chamado versions, onde todas as diferentes versões de migração são armazenadas.

A parte b9198aca3963 no nome do arquivo do script de migração é um ID aleatório gerado para identificar diferentes migrações para que seja diferente para você.

Para entender como funciona um script de migração, abra o seu com o seguinte comando. Certifique-se de substituir b9198aca3963 pelo ID que foi gerado para você e que você está em seu diretório raiz flask_app:

nano migrations/versions/b9198aca3963_initial_migration.py

Além das importações internas e de uma configuração, este script de migração inicial terá duas funções principais semelhantes às seguintes:

def upgrade():
    # ### commands auto-generated by Alembic - please adjust! ###
    op.create_table('products',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto-generated by Alembic - please adjust! ###
    op.drop_table('products')
    # ### end Alembic commands ###
  • A função upgrade() altera o banco de dados com base nas modificações detectadas pelo comando flask db migrate. Neste caso, uma nova tabela chamada produtos foi detectada, então a função upgrade() cria a nova tabela com as colunas especificadas em Produto() modelo de banco de dados que você declarou no arquivo app.py.

  • A função downgrade() remove as alterações e restaura o estado do banco de dados como estava antes da atualização. Neste exemplo, o estado anterior é restaurado excluindo a tabela produtos que será criada pela função upgrade().

Observação: lembre-se de que os scripts de migração são códigos gerados automaticamente, que podem conter alguns erros dependendo da complexidade das alterações realizadas antes da migração. Portanto, você deve ler e ajustar cuidadosamente seus scripts de migração para garantir a precisão e a execução adequada.

Com o script de migração pronto, agora você pode usá-lo para realizar uma atualização inicial. Isso criará o arquivo de banco de dados app.db e a tabela products. Para fazer isso, execute o seguinte comando no diretório flask_app:

flask db upgrade

A saída será semelhante à seguinte:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> b9198aca3963, initial migration

Isso informa que a atualização foi executada com sucesso.

Um novo arquivo de banco de dados app.db será adicionado à sua pasta instance dentro do diretório flask_app.

Nota: A primeira migração é equivalente a usar db.create_all() no shell Flask.

Se você introduzir o Flask-Migrate em um projeto existente com um banco de dados existente, então o flask db upgrade falhará porque já existe um arquivo de banco de dados. Nesse caso, use flask db stamp para marcar o banco de dados como atualizado em vez de flask db upgrade.

Com o banco de dados e a tabela produtos criados, agora você pode adicionar alguns produtos ao seu banco de dados. Você fará isso a seguir.

Preenchendo o banco de dados

Agora você usará o shell Flask para inserir alguns itens na tabela products.

Com seu ambiente virtual ativado, execute o seguinte comando para acessar o shell Flask:

flask shell

Dentro do shell interativo, execute o seguinte código:

from app import db, Product

apple = Product(name="Apple")
orange = Product(name="Orange")
banana = Product(name="Banana")
db.session.add_all([apple, orange, banana])
db.session.commit()

Este código faz o seguinte:

  • Importa o objeto db e o modelo Product do arquivo app.py.
  • Cria três instâncias da classe Product() passando um nome para cada produto.
  • Adiciona os novos produtos à sessão do banco de dados usando o método db.session.add_all().
  • Aplica alterações ao banco de dados usando o método db.session.commit().

Para obter mais informações sobre como usar o Flask-SQLAlchemy, consulte Como usar o Flask-SQLAlchemy para interagir com bancos de dados em um aplicativo Flask.

Saia do shell do Flask:

exit()

Para garantir que os itens do produto foram adicionados ao banco de dados, reinicie o shell Flask com uma memória nova:

flask shell

Em seguida, execute o seguinte código para percorrer os itens da tabela de produtos:

from app import db, Product

for p in Product.query.all():
    print(p.name, p.id)

Aqui, você importa o objeto db e o modelo Produto; então você usa um loop for no resultado do método query.all() para acessar cada item na tabela de produtos e imprimir o nome e ID de cada item.

A saída será a seguinte:

Apple 1
Orange 2
Banana 3

Você migrou seu banco de dados com êxito e preencheu a tabela de produtos com três itens. Modificaremos o esquema do banco de dados e adicionaremos uma nova coluna price à tabela products e, em seguida, migraremos e atualizaremos nosso banco de dados preservando esses dados usando Flask-Migrate.

Passo 04 - Modificando o Modelo de Banco de Dados

Agora você tem uma tabela de produtos em seu banco de dados com alguns itens armazenados nela. Nesta etapa, você usará o Flask-Migrate para adicionar uma coluna price à tabela products. Suponha que você queira fazer isso sem um gerenciador de migração de banco de dados. Nesse caso, você deve primeiro excluir toda a tabela produtos e criá-la novamente com a nova coluna, resultando na perda de todos os itens de produtos existentes. No entanto, com Flask-Migrate, você pode adicionar uma nova coluna enquanto preserva os dados existentes.

Adicionando uma nova coluna de preço

Para adicionar uma coluna de preço à sua tabela de produtos, abra o arquivo app.py e modifique o modelo de banco de dados Product que define o esquema da tabela.

Dentro do diretório flask_app, abra app.py para modificação:

nano app.py

Edite a classe Product() adicionando uma nova coluna inteira chamada price:

class Product(db.Model):
    __tablename__ = 'products'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    price = db.Column(db.Integer)

    def __repr__(self):
        return f'<Product {self.name}>'

Salve e feche o arquivo.

A modificação do esquema do banco de dados foi concluída. Você gerará um novo script de migração para aplicá-lo ao banco de dados e, ao mesmo tempo, evitar a perda de dados.

Gerar uma migração

Depois de modificar seu modelo de banco de dados, execute o comando flask db migrate para Flask-Migrate detectar sua modificação e gerar um novo script de migração. Certifique-se de adicionar uma mensagem descrevendo o que você modificou:

flask db migrate -m "add price column"

Semelhante à migração inicial, você receberá uma saída como esta:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added column 'products.price'
  Generating
  flask_app/migrations/versions/7ad34929a0f2_add_price_column.py
  ...  done

Isso informa que uma nova coluna foi detectada e um script de migração foi criado.

Lembre-se de revisar o script de migração gerado. Neste caso as funções principais upgrade() e downgrade() serão assim:

def upgrade():
    # ### commands auto-generated by Alembic - please adjust! ###
    with op.batch_alter_table('products', schema=None) as batch_op:
        batch_op.add_column(sa.Column('price', sa.Integer(), nullable=True))
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto-generated by Alembic - please adjust! ###
    with op.batch_alter_table('products', schema=None) as batch_op:
        batch_op.drop_column('price')
    # ### end Alembic commands ###

Aqui, a função upgrade() altera a tabela produtos e adiciona uma coluna preço. A função downgrade() remove a coluna price(), restaurando seu banco de dados para a versão anterior.

Atualizando o banco de dados

Após modificar seu modelo de banco de dados, gere um script de migração baseado nesta modificação. Agora você pode aplicar as alterações ao banco de dados usando o comando upgrade, que executa a função upgrade() no script de migração mais recente.

flask db upgrade

A saída informará que o banco de dados foi movido da versão anterior para uma nova versão com a "adicionar coluna de preço" como uma mensagem de migração.

Para testar se a coluna price foi adicionada, execute o shell Flask:

flask shell

Em seguida, percorra os itens do produto no banco de dados e imprima os valores das colunas:

from app import db, Product

for p in Product.query.all():
    print(p.name, p.id, p.price)

A saída deve ser a seguinte:

Apple 1 None
Orange 2 None
Banana 3 None

Os valores None na saída refletem os valores da nova coluna de preço e agora você pode modificá-los conforme desejar para refletir os preços de seus produtos.

Se você receber um erro ao executar o loop anterior, a coluna de preço não foi adicionada corretamente e você deve revisar cuidadosamente as etapas anteriores, garantindo que executou corretamente todas as ações necessárias.

Saia do shell do Flask:

exit()

O banco de dados agora foi migrado para uma nova versão. A seguir, você fará o downgrade do banco de dados e removerá a coluna price para demonstrar como restaurar um estado anterior do banco de dados.

Passo 05 - Downgrade do Banco de Dados

Na etapa anterior, você atualizou a versão inicial do banco de dados e adicionou uma coluna price à tabela products. Para demonstrar como restaurar um estado anterior ao gerenciar migrações de banco de dados, você fará o downgrade do seu banco de dados atual e removerá a coluna price da tabela products.

Para fazer downgrade do seu banco de dados e restaurar sua versão anterior, execute o seguinte comando dentro do diretório flask_app:

flask db downgrade

A saída informará que a versão do banco de dados foi alterada e a versão anterior foi restaurada.

Para testar se a coluna price foi removida, abra seu shell Flask:

flask shell

Em seguida, execute uma consulta no modelo Product para obter todos os itens da tabela de produtos:

from app import db, Product
Product.query.all()

Você deverá receber um erro indicando que a tabela produtos não tem coluna preço:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: products.price
[SQL: SELECT products.id AS products_id, products.name AS products_name, products.price AS products_price
FROM products]
(Background on this error at: https://sqlalche.me/e/20/e3q8)

Isso confirma que a coluna price foi removida com sucesso e que o downgrade do banco de dados foi bem-sucedido, e o erro ocorre porque você ainda tem uma coluna price definida no Product( ) dentro do arquivo app.py.

Para corrigir esse erro, saia do shell do Flask:

exit()

Em seguida, abra app.py dentro do diretório flask_app:

nano app.py

Modifique o modelo Product() removendo a declaração da coluna price para que a versão final fique assim:

class Product(db.Model):
    __tablename__ = 'products'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))

    def __repr__(self):
        return f'<Product {self.name}>'

Salve e feche o arquivo.

Para testar se o erro foi corrigido, abra o shell do Flask:

flask shell

Em seguida, consulte todos os produtos:

from app import db, Product

Product.query.all()

A saída deve ser a seguinte:

[<Product Apple>, <Product Orange>, <Product Banana>]

Isso indica que o erro foi corrigido.

Finalmente, você pode excluir o arquivo de migração que contém add_price_column em seu nome de arquivo se não precisar mais dele:

rm migrations/versions/7ad34929a0f2_add_price_column.py

Com isso, você fez o downgrade do seu banco de dados com sucesso e o restaurou para sua versão anterior.

Conclusão

Você criou um pequeno aplicativo Flask com um banco de dados e o integrou ao Flask-Migrate. Você aprendeu a modificar seus modelos de banco de dados, atualizá-los para uma nova versão e fazer downgrade deles para uma versão anterior.

Em geral, você pode seguir as seguintes etapas para gerenciar as migrações de banco de dados à medida que desenvolve seus aplicativos Flask:

  1. Modifique os modelos de banco de dados.

  2. Gere um script de migração com o comando flask db migrate.

  3. Revise o script de migração gerado e corrija-o, se necessário.

  4. Aplique as alterações ao banco de dados com o comando flask db upgrade.

  5. Para restaurar uma versão anterior do banco de dados, use o comando flask db downgrade.

Consulte a documentação do Flask-Migrate para obter mais informações.

Se você quiser ler mais sobre o Flask, confira os outros tutoriais da série Como construir aplicativos da Web com Flask.

Artigos relacionados: