Assista agora Este tutorial tem um curso em vídeo relacionado criado pela equipe Real Python. Assista junto com o tutorial escrito para aprofundar sua compreensão:Django Migrations 101
Desde a versão 1.7, o Django vem com suporte embutido para migrações de banco de dados. No Django, as migrações de banco de dados geralmente andam de mãos dadas com os modelos:sempre que você codifica um novo modelo, você também gera uma migração para criar a tabela necessária no banco de dados. No entanto, as migrações podem fazer muito mais.
Você aprenderá como as migrações do Django funcionam e como tirar o máximo proveito delas ao longo de quatro artigos e um vídeo:
- Parte 1:Migrações do Django:uma cartilha (artigo atual)
- Parte 2:aprofundando as migrações
- Parte 3:Migrações de dados
- Vídeo:migrações do Django 1.7 - primer
Neste artigo, você se familiarizará com as migrações do Django e aprenderá o seguinte:
- Como criar tabelas de banco de dados sem escrever nenhum SQL
- Como modificar automaticamente seu banco de dados depois de alterar seus modelos
- Como reverter as alterações feitas em seu banco de dados
Bônus grátis: Clique aqui para obter acesso a um Guia de Recursos de Aprendizagem Django (PDF) gratuito que mostra dicas e truques, bem como armadilhas comuns a serem evitadas ao construir aplicativos web Python + Django.
Os problemas que as migrações resolvem
Se você é novo no Django ou no desenvolvimento web em geral, pode não estar familiarizado com o conceito de migrações de banco de dados, e pode não parecer óbvio porque elas são uma boa ideia.
Primeiro, vamos definir rapidamente alguns termos para garantir que todos estejam na mesma página. O Django foi projetado para trabalhar com um banco de dados relacional, armazenado em um sistema de gerenciamento de banco de dados relacional como PostgreSQL, MySQL ou SQLite.
Em um banco de dados relacional, os dados são organizados em tabelas. Uma tabela de banco de dados tem um certo número de colunas, mas pode ter qualquer número de linhas. Cada coluna tem um tipo de dados específico, como uma string de um determinado comprimento máximo ou um número inteiro positivo. A descrição de todas as tabelas com suas colunas e seus respectivos tipos de dados é chamada de esquema de banco de dados.
Todos os sistemas de banco de dados suportados pelo Django usam a linguagem SQL para criar, ler, atualizar e excluir dados em um banco de dados relacional. O SQL também é usado para criar, alterar e excluir as próprias tabelas do banco de dados.
Trabalhar diretamente com SQL pode ser bastante complicado, então para facilitar sua vida, o Django vem com um mapeador relacional de objeto, ou ORM, abreviado. O ORM mapeia o banco de dados relacional para o mundo da programação orientada a objetos. Em vez de definir tabelas de banco de dados em SQL, você escreve modelos Django em Python. Seus modelos definem campos de banco de dados, que correspondem às colunas em suas tabelas de banco de dados.
Aqui está um exemplo de como uma classe de modelo do Django é mapeada para uma tabela de banco de dados:
Mas apenas definir uma classe de modelo em um arquivo Python não faz uma tabela de banco de dados aparecer magicamente do nada. Criar as tabelas de banco de dados para armazenar seus modelos Django é o trabalho de uma migração de banco de dados. Além disso, sempre que você fizer uma alteração em seus modelos, como adicionar um campo, o banco de dados também deverá ser alterado. As migrações também lidam com isso.
Aqui estão algumas maneiras pelas quais as migrações do Django facilitam sua vida.
Fazendo alterações no banco de dados sem SQL
Sem migrações, você teria que se conectar ao seu banco de dados e digitar vários comandos SQL ou usar uma ferramenta gráfica como PHPMyAdmin para modificar o esquema do banco de dados toda vez que quisesse alterar sua definição de modelo.
No Django, as migrações são escritas principalmente em Python, então você não precisa conhecer nenhum SQL, a menos que tenha casos de uso realmente avançados.
Evitando a repetição
Criar um modelo e depois escrever SQL para criar as tabelas de banco de dados para ele seria repetitivo.
As migrações são geradas a partir de seus modelos, garantindo que você não se repita.
Garantindo a sincronização das definições do modelo e do esquema de banco de dados
Normalmente, você tem várias instâncias do seu banco de dados, por exemplo, um banco de dados para cada desenvolvedor em sua equipe, um banco de dados para teste e um banco de dados com dados ao vivo.
Sem migrações, você terá que realizar qualquer alteração de esquema em cada um de seu banco de dados e terá que acompanhar quais alterações já foram feitas em qual banco de dados.
Com o Django Migrations, você pode manter facilmente vários bancos de dados sincronizados com seus modelos.
Acompanhamento da alteração do esquema do banco de dados no controle de versão
Um sistema de controle de versão, como o Git, é excelente para código, mas não tanto para esquemas de banco de dados.
Como as migrações são Python simples no Django, você pode colocá-las em um sistema de controle de versão como qualquer outro pedaço de código.
Até agora, esperamos que você esteja convencido de que as migrações são uma ferramenta útil e poderosa. Vamos começar a aprender como liberar esse poder.
Configurando um projeto Django
Ao longo deste tutorial, você trabalhará em um aplicativo simples de rastreamento de Bitcoin como um projeto de exemplo.
O primeiro passo é instalar o Django. Veja como você faz isso no Linux ou macOS X usando um ambiente virtual:
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install "Django==2.1.*"
...
Successfully installed Django-2.1.3
Agora você criou um novo ambiente virtual e o ativou, assim como instalou o Django nesse ambiente virtual.
Observe que no Windows, você executaria
env/bin/activate.bat
em vez de source env/bin/activate
para ativar seu ambiente virtual. Para facilitar a leitura, os exemplos de console não incluirão o
(env)
parte do prompt a partir de agora. Com o Django instalado, você pode criar o projeto usando os seguintes comandos:
$ django-admin.py startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_data
Isso fornece um projeto simples e um aplicativo chamado
historical_data
. Agora você deve ter esta estrutura de diretórios:bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ │ └── __init__.py
| |
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
└── manage.py
Dentro do
bitcoin_tracker
diretório, existem dois subdiretórios:bitcoin_tracker
para arquivos de todo o projeto e historical_data
contendo arquivos para o aplicativo que você criou. Agora, para criar um modelo, adicione esta classe em
historical_data/models.py
:class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.PositiveIntegerField()
Este é o modelo básico para acompanhar os preços do Bitcoin.
Além disso, não se esqueça de adicionar o aplicativo recém-criado a
settings.INSTALLED_APPS
. Abra bitcoin_tracker/settings.py
e anexar historical_data
para a lista INSTALLED_APPS
, assim:INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'historical_data',
]
As outras configurações são boas para este projeto. Este tutorial pressupõe que seu projeto esteja configurado para usar um banco de dados SQLite, que é o padrão.
Criando migrações
Com o modelo criado, a primeira coisa que você precisa fazer é criar uma migração para ele. Você pode fazer isso com o seguinte comando:
$ python manage.py makemigrations historical_data
Migrations for 'historical_data':
historical_data/migrations/0001_initial.py
- Create model PriceHistory
Observação: Especificando o nome do aplicativo,
historical_data
, é opcional. Deixá-lo desligado cria migrações para todos os aplicativos. Isso cria o arquivo de migração que instrui o Django sobre como criar as tabelas de banco de dados para os modelos definidos em seu aplicativo. Vamos dar outra olhada na árvore de diretórios:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── migrations/
│ │ ├── 0001_initial.py
│ │ └── __init__.py
| |
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
├── db.sqlite3
└── manage.py
Como você pode ver, as
migrations
agora contém um novo arquivo:0001_initial.py
. Observação: Você pode notar que executar o
makemigrations
comando também criou o arquivo db.sqlite3
, que contém seu banco de dados SQLite. Quando você tenta acessar um arquivo de banco de dados SQLite3 inexistente, ele será criado automaticamente.
Esse comportamento é exclusivo do SQLite3. Se você usa qualquer outro backend de banco de dados como PostgreSQL ou MySQL, você deve criar o banco de dados você mesmo antes executando
makemigrations
. Você pode dar uma olhada no banco de dados com o
dbshell
comando de gestão. No SQLite, o comando para listar todas as tabelas é simplesmente .tables
:$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
sqlite>
O banco de dados ainda está vazio. Isso mudará quando você aplicar a migração. Digite
.quit
para sair do shell SQLite. Aplicando migrações
Você agora criou a migração, mas para realmente fazer qualquer alteração no banco de dados, você deve aplicá-la com o comando de gerenciamento
migrate
:$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying historical_data.0001_initial... OK
Applying sessions.0001_initial... OK
Tem muita coisa acontecendo aqui! De acordo com a saída, sua migração foi aplicada com sucesso. Mas de onde vêm todas as outras migrações?
Lembre-se da configuração
INSTALLED_APPS
? Alguns dos outros aplicativos listados também vêm com migrações, e o migrate
O comando de gerenciamento aplica as migrações para todos os aplicativos instalados por padrão. Dê outra olhada no banco de dados:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups historical_data_pricehistory
auth_user_user_permissions
sqlite>
Agora há várias tabelas. Seus nomes dão uma idéia de seu propósito. A migração que você gerou na etapa anterior criou o
historical_data_pricehistory
tabela. Vamos inspecioná-lo usando o .schema
comando:sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory"(
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
O
.schema
comando imprime o CREATE
instrução que você executaria para criar a tabela. O parâmetro --indent
formata bem. Mesmo que você não esteja familiarizado com a sintaxe SQL, você pode ver que o esquema do historical_data_pricehistory
tabela reflete os campos do PriceHistory
modelo. Há uma coluna para cada campo e uma coluna adicional
id
para a chave primária, que o Django cria automaticamente, a menos que você especifique explicitamente uma chave primária em seu modelo. Veja o que acontece se você executar o
migrate
comando novamente:$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
Nada! O Django lembra quais migrações já foram aplicadas e não tenta executá-las novamente.
Vale a pena notar que você também pode limitar o
migrate
comando de gerenciamento para um único aplicativo:$ python manage.py migrate historical_data
Operations to perform:
Apply all migrations: historical_data
Running migrations:
No migrations to apply.
Como você pode ver, o Django agora aplica migrações apenas para o
historical_data
aplicativo. Ao executar as migrações pela primeira vez, é uma boa ideia aplicar todas as migrações para garantir que seu banco de dados contenha as tabelas necessárias para os recursos que você pode considerar como garantidos, como autenticação de usuário e sessões.
Alterando modelos
Seus modelos não são imutáveis. Seus modelos mudarão à medida que seu projeto Django ganhar mais recursos. Você pode adicionar ou remover campos ou alterar seus tipos e opções.
Quando você altera a definição de um modelo, as tabelas de banco de dados usadas para armazenar esses modelos também precisam ser alteradas. Se suas definições de modelo não corresponderem ao seu esquema de banco de dados atual, você provavelmente encontrará um
django.db.utils.OperationalError
. Então, como você altera as tabelas do banco de dados? Criando e aplicando uma migração.
Ao testar seu rastreador Bitcoin, você percebe que cometeu um erro. As pessoas estão vendendo frações de um Bitcoin, então o campo
volume
deve ser do tipo DecimalField
em vez de PositiveIntegerField
. Vamos mudar o modelo para ficar assim:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.DecimalField(max_digits=7, decimal_places=3)
Sem migrações, você teria que descobrir a sintaxe SQL para transformar um
PositiveIntegerField
em um DecimalField
. Felizmente, o Django cuidará disso para você. Basta dizer para fazer migrações:$ python manage.py makemigrations
Migrations for 'historical_data':
historical_data/migrations/0002_auto_20181112_1950.py
- Alter field volume on pricehistory
Observação: O nome do arquivo de migração (
0002_auto_20181112_1950.py
) é baseado na hora atual e será diferente se você acompanhar em seu sistema. Agora você aplica essa migração ao seu banco de dados:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying historical_data.0002_auto_20181112_1950... OK
A migração foi aplicada com sucesso, então você pode usar
dbshell
para verificar se as alterações tiveram efeito:$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" decimal NOT NULL
);
Se você comparar o novo esquema com o esquema que viu anteriormente, notará que o tipo do
volume
coluna mudou de integer
para decimal
para refletir a mudança do volume
campo no modelo de PositiveIntegerField
para DecimalField
. Como listar as migrações
Se você quer saber quais migrações existem em um projeto Django, você não precisa pesquisar as
migrations
diretórios de seus aplicativos instalados. Você pode usar o showmigrations
comando:$ ./manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
historical_data
[X] 0001_initial
[X] 0002_auto_20181112_1950
sessions
[X] 0001_initial
Isso lista todos os aplicativos no projeto e as migrações associadas a cada aplicativo. Além disso, ele colocará um grande
X
ao lado das migrações que já foram aplicadas. Para nosso pequeno exemplo, o
showmigrations
command não é particularmente empolgante, mas é útil quando você começa a trabalhar em uma base de código existente ou trabalha em uma equipe onde você não é a única pessoa que adiciona migrações. Desaplicando migrações
Agora você sabe como fazer alterações em seu esquema de banco de dados criando e aplicando migrações. Em algum momento, você pode querer desfazer as alterações e voltar para um esquema de banco de dados anterior porque:
- Deseja testar uma migração que um colega escreveu
- Perceba que uma alteração que você fez foi uma má ideia
- Trabalhe em vários recursos com diferentes alterações de banco de dados em paralelo
- Deseja restaurar um backup que foi criado quando o banco de dados ainda tinha um esquema mais antigo
Felizmente, as migrações não precisam ser uma via de mão única. Em muitos casos, os efeitos de uma migração podem ser desfeitos ao não aplicar uma migração. Para cancelar a aplicação de uma migração, você deve chamar
migrate
com o nome do aplicativo e o nome da migração antes a migração que você deseja cancelar. Se você quiser reverter a migração
0002_auto_20181112_1950
em seu historical_data
app, você tem que passar 0001_initial
como um argumento para o migrate
comando:$ python manage.py migrate historical_data 0001_initial
Operations to perform:
Target specific migration: 0001_initial, from historical_data
Running migrations:
Rendering model states... DONE
Unapplying historical_data.0002_auto_20181112_1950... OK
A migração não foi aplicada, o que significa que as alterações no banco de dados foram revertidas.
Desaplicar uma migração não remove seu arquivo de migração. Na próxima vez que você executar o
migrate
comando, a migração será aplicada novamente. Cuidado: Não confunda migrações não aplicadas com a operação de desfazer que você está acostumado no seu editor de texto favorito.
Nem todas as operações de banco de dados podem ser completamente revertidas. Se você remover um campo de um modelo, criar uma migração e aplicá-la, o Django removerá a respectiva coluna do banco de dados.
Desaplicar essa migração recriará a coluna, mas não trará de volta os dados armazenados nessa coluna!
Quando você está lidando com nomes de migração, o Django economiza algumas teclas ao não forçar você a soletrar o nome completo da migração. Ele precisa apenas o suficiente do nome para identificá-lo exclusivamente.
No exemplo anterior, bastaria executar
python manage.py migrate historical_data 0001
. Migrações de nomenclatura
No exemplo acima, o Django criou um nome para a migração baseado no timestamp—algo como
*0002_auto_20181112_1950
. Se você não estiver satisfeito com isso, poderá usar o --name
parâmetro para fornecer um nome personalizado (sem o .py
extensão). Para experimentar isso, primeiro você precisa remover a migração antiga. Você já o desinstalou, então você pode excluir o arquivo com segurança:
$ rm historical_data/migrations/0002_auto_20181112_1950.py
Agora você pode recriá-lo com um nome mais descritivo:
$ ./manage.py makemigrations historical_data --name switch_to_decimals
Isso criará a mesma migração de antes, exceto com o novo nome de
0002_switch_to_decimals
. Conclusão
Você cobriu um pouco do terreno neste tutorial e aprendeu os fundamentos das migrações do Django.
Para recapitular, as etapas básicas para usar as migrações do Django são assim:
- Criar ou atualizar um modelo
- Execute
./manage.py makemigrations <app_name>
- Execute
./manage.py migrate
para migrar tudo ou./manage.py migrate <app_name>
para migrar um aplicativo individual - Repita conforme necessário
É isso! Esse fluxo de trabalho funcionará na maioria das vezes, mas se as coisas não funcionarem conforme o esperado, você também saberá como listar e desaplicar migrações.
Se você já criou e modificou suas tabelas de banco de dados com SQL escrito à mão, agora você se tornou muito mais eficiente ao delegar esse trabalho às migrações do Django.
No próximo tutorial desta série, você se aprofundará no tópico e aprenderá como as migrações do Django funcionam nos bastidores.
Bônus grátis: Clique aqui para obter acesso a um Guia de Recursos de Aprendizagem Django (PDF) gratuito que mostra dicas e truques, bem como armadilhas comuns a serem evitadas ao construir aplicativos web Python + Django.
Saúde!