Django South Workflow – Trabalhando em equipe – Parte 1

Este artigo faz parte da série “Django South Workflow – Trabalhando em equipe“. Sugiro que o leia antes para entender sobre o que se trata e também conhecer os outros artigos da série.

Neste, vamos abordar o erro: “Table already exists”

Como o próprio nome sugere, o erro ocorre quando o South tenta criar uma tabela que já existe. Mas porque ele se comporta assim? Vamos entender…

Fluxo causador – Entendendo o problema

Imagem 1

Imagine o senário acima. O Dev1 editou o model da aplicação “app”, adicionando uma classe chamada “Pessoa”. Ao mesmo tempo o Dev2, no seu repositório, editou o mesmo model, porém adicionando a classe “Veiculo”.

Se o Dev1 executar o “schemamigration app –auto”, obterá como resultado a migração para adicionar sua classe e o resultado ficará da seguinte forma:

#0002_auto__add_pessoa.py
class Migration(SchemaMigration):
    def forwards(self, orm):
        # Adding model 'Pessoa'
        db.create_table('app_pessoa', (
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('nome', self.gf('django.db.models.fields.CharField')(max_length=255)),
        ))
        db.send_create_signal('app', ['Pessoa'])
    ....
    models = {
        'app.loja': {
            'nome': ('django.db.models.fields.CharField', [], {'max_length': '255'})
        },
        'app.pessoa': {
            'Meta': {'object_name': 'Pessoa'},
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'nome': ('django.db.models.fields.CharField', [], {'max_length': '255'})
        }
    }

Já no Dev2, para o mesmo comando, o resultado será o seguinte:

#0002_auto__add_veiculo.py
class Migration(SchemaMigration):

    def forwards(self, orm):
        # Adding model 'Veiculo'
        db.create_table('app_veiculo', (
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('cor', self.gf('django.db.models.fields.CharField')(max_length=255)),
        ))
        db.send_create_signal('app', ['Veiculo'])
    ....
    models = {
        'app.loja': {
            'nome': ('django.db.models.fields.CharField', [], {'max_length': '255'})
        },
        'app.veiculo': {
            'Meta': {'object_name': 'Veiculo'},
            'cor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
        }
    }

Agora imagine os dois enviando seus arquivos para o repositório principal. Como ficará?

$ ls app/migrations
    app/migrations/0001_initial.py
    app/migrations/0002_auto__add_pessoa.py
    app/migrations/0002_auto__add_veiculo.py
    app/migrations/__init__.py

Aparentemente, tudo ok. Temos as duas migrações e o migrate funciona perfeitamente.

$ ./manage.py migrate app
Running migrations for app:
 - Migrating forwards to 0002_auto__add_veiculo.
 > app:0002_auto__add_pessoa
 > app:0002_auto__add_veiculo
 - Loading initial data for app.
Installed 0 object(s) from 0 fixture(s)

Mas seguindo o fluxo de desenvolvimento, o Dev3 precisou alterar a classe “Veiculo” e adicionou o atributo “placa”.


Após terminar sua alteração e executar o comando “schemamigration –auto”, aparece o nosso primeiro problema.

$ ./manage.py schemamigration app --auto
 + Added model app.Pessoa
 + Added field placa on app.Veiculo
Created 0003_auto__add_pessoa__add_field_veiculo_placa.py.

Normalmente este passo passa despercebido pelo desenvolvedor. Mas sendo atento, fica possível identificar que algo não está certo. Perceba que além de adicionar o atributo “placa”, erroneamente o South adicionou a classe “Pessoa” na migração.

Ao executar o comando “migrate”, receberemos o nosso erro:

$ ./manage.py migrate app
Running migrations for app:
 - Migrating forwards to 0003_auto__add_pessoa__add_field_veiculo_placa.
 > app:0003_auto__add_pessoa__add_field_veiculo_placa
FATAL ERROR - ....
The error was: (1050, "Table 'app_pessoa' already exists")

Porque o South recriou essa migração, se ela já consta na lista de migrações e inclusive já foi executada?

Quando executamos o “schemamigration”, o South analisou a variável “model” da última migração (ordenado alfabeticamente) contida no diretório de migrações e a comparou com o arquivo “models.py” da aplicação.

Como a última migração é a migração do Dev2 (0002_auto__add_veiculo), ele considerou que anteriormente a classe “Pessoa” não existia, portanto a adicionou.

Correção

Existem algumas formas de se corrigir este erro, para evitar erros de digitação ou falta de atenção, escolhi a maneira que considero mais segura, utilizando os recursos do próprio South:

 – Primeiro passo – Desfaça as alterações

Apague a migração que você criou, faça um backup do seu “models.py” e restaure a versão anterior sem as suas alterações.

 – Segundo passo – Faça o South saber como está o Model

Precisamos informar corretamente ao South como está o “models.py” atual, portanto vamos gerar uma migração vazia, porém com a variável “model” atualizada.  Para isso, o South disponibiliza o parâmetro ‘–empty’.

$./manage.py schemamigration app model_checkpoint --empty
.
– Terceiro passo – Refaça sua alteração

Agora que o South já entende nosso model corretamente, volte com suas alterações no “models.py” e basta rodar o “schemamigration” normal:

$./manage.py schemamigration app --auto

Problema resolvido, basta rodar o “migrate”.

Publicidade

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s