Pergunta

Estou planejando renomear vários modelos em um projeto Django existente, onde existem muitos outros modelos que possuem relacionamentos de chave estrangeira com os modelos que gostaria de renomear.Tenho quase certeza de que isso exigirá várias migrações, mas não tenho certeza do procedimento exato.

Digamos que eu comece com os seguintes modelos em um aplicativo Django chamado myapp:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Eu quero renomear o Foo modelo porque o nome realmente não faz sentido e está causando confusão no código, e Bar daria um nome muito mais claro.

Pelo que li na documentação de desenvolvimento do Django, estou assumindo a seguinte estratégia de migração:

Passo 1

Modificar models.py:

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_ridonkulous = models.BooleanField()

Note o AnotherModel nome do campo para foo não muda, mas a relação é atualizada para o Bar modelo.Meu raciocínio é que eu não deveria mudar muito de uma vez e que se eu mudasse o nome deste campo para bar Eu correria o risco de perder os dados dessa coluna.

Passo 2

Crie uma migração vazia:

python manage.py makemigrations --empty myapp

etapa 3

Edite o Migration class no arquivo de migração criado na etapa 2 para adicionar o RenameModel operação para a lista de operações:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

Passo 4

Aplique a migração:

python manage.py migrate

Etapa 5

Edite os nomes dos campos relacionados em models.py:

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

Etapa 6

Crie outra migração vazia:

python manage.py makemigrations --empty myapp

Etapa 7

Edite o Migration class no arquivo de migração criado na etapa 6 para adicionar o RenameField operação(ões) para qualquer nome de campo relacionado à lista de operações:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_rename_fields'),  # <-- is this okay?
    ]

    operations = [
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

Etapa 8

Aplique a 2ª migração:

python manage.py migrate

Além de atualizar o restante do código (visualizações, formulários, etc.) para refletir os novos nomes de variáveis, é basicamente assim que a nova funcionalidade de migração funcionaria?

Além disso, isso parece ser uma série de etapas.As operações de migração podem ser condensadas de alguma forma?

Obrigado!

Foi útil?

Solução

Então, quando tentei isso, parece que você pode condensar as etapas 3 a 7:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'), 
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar'),
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

Você pode receber alguns erros se não atualizar os nomes de onde foram importados, por exemplo.admin.py e arquivos de migração ainda mais antigos (!).

Atualizar:Como César menciona, versões mais recentes do Django geralmente são capazes de detectar e perguntar se um modelo foi renomeado.Então tente manage.py makemigrations primeiro e depois verifique o arquivo de migração.

Outras dicas

No início, pensei que o método de Fiver funcionasse para mim porque a migração funcionou bem até a etapa 4.No entanto, as alterações implícitas de 'ForeignKeyField(Foo)' para 'ForeignKeyField(Bar)' não foram relacionadas em nenhuma migração.É por isso que a migração falhou quando quis renomear os campos de relacionamento (etapas 5 a 8).Isso pode ser devido ao fato de que meu 'AnotherModel' e 'YetAnotherModel' são despachados em outros aplicativos no meu caso.

Então consegui renomear meus modelos e campos de relacionamento seguindo as etapas abaixo:

Eu adaptei o método de esse e particularmente o truque do otranzer.

Então, como Fiver, digamos que temos em meu aplicativo:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

E em meu outro aplicativo:

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Passo 1:

Transforme cada OneToOneField(Foo) ou ForeignKeyField(Foo) em IntegerField().(Isso manterá o id do objeto Foo relacionado como valor do campo inteiro).

class AnotherModel(models.Model):
    foo = models.IntegerField()
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.IntegerField()
    is_ridonkulous = models.BooleanField()

Então

python manage.py makemigrations

python manage.py migrate

Passo 2:(Como a etapa 2-4 do Fiver)

Alterar o nome do modelo

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

Crie uma migração vazia:

python manage.py makemigrations --empty myapp

Em seguida, edite como:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

Eventualmente

python manage.py migrate

Etapa 3:

Transforme seu IntegerField() em seu ForeignKeyField ou OneToOneField anterior, mas com o novo modelo de barra.(O campo inteiro anterior estava armazenando o id, então o Django entende isso e restabelece a conexão, o que é legal.)

class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_ridonkulous = models.BooleanField()

Então faça:

python manage.py makemigrations 

Muito importante, nesta etapa você deve modificar cada nova migração e adicionar a dependência nas migrações RenameModel Foo-> Bar.Portanto, se AnotherModel e YetAnotherModel estiverem em myotherapp, a migração criada em myotherapp deverá ser semelhante a esta:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
        ('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
    ]

    operations = [
        migrations.AlterField(
            model_name='anothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar'),
        ),
        migrations.AlterField(
            model_name='yetanothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar')
        ),
    ]

Então

python manage.py migrate

Passo 4:

Eventualmente você pode renomear seus campos

class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_ridonkulous = models.BooleanField()

e depois renomeie automaticamente

python manage.py makemigrations

(Django deve perguntar se você realmente renomeou o nome do modelo, diga sim)

python manage.py migrate

E é isso!

Isso funciona no Django1.8

Eu precisava fazer a mesma coisa.Mudei o modelo de uma vez (ou seja, etapa 1 e etapa 5 juntas).Em seguida, criei uma migração de esquema, mas editei-a para ficar assim:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')

Isso funcionou perfeitamente.Todos os meus dados existentes apareceram, todas as outras tabelas referenciadas Bar bem.

daqui: https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/

Para o Django 1.10, consegui alterar dois nomes de classes de modelo (incluindo ForeignKey e com dados) simplesmente executando Makemigrations e depois Migrate para o aplicativo.Para a etapa Makemigrations, tive que confirmar que queria alterar os nomes das tabelas.O Migrate alterou os nomes das tabelas sem problemas.

Em seguida, alterei o nome do campo ForeignKey para corresponder e novamente fui solicitado pela Makemigrations para confirmar que queria alterar o nome.Migrar do que fazer a alteração.

Então fiz isso em duas etapas, sem nenhuma edição especial de arquivo.Recebi erros no início porque esqueci de alterar o arquivo admin.py, conforme mencionado por @wasibigeek.

Eu também enfrentei o problema conforme descrito por v.thorey e descobri que sua abordagem é muito útil, mas pode ser condensada em menos etapas que são, na verdade, as etapas 5 a 8, como Fiver descreveu, sem as etapas 1 a 4, exceto que a etapa 7 precisa ser alterada como meu abaixo da etapa 3.As etapas gerais são as seguintes:

Passo 1:Edite os nomes dos campos relacionados em models.py

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

Passo 2:Crie uma migração vazia

python manage.py makemigrations --empty myapp

Etapa 3:Edite a classe Migration no arquivo de migração criado na Etapa 2

class Migration(migrations.Migration):

dependencies = [
    ('myapp', '0001_initial'), 
]

operations = [
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.RenameModel('Foo', 'Bar'),
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.RenameField('AnotherModel', 'foo', 'bar'),
    migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]

Passo 4:Aplicar a migração

python manage.py migrate

Feito

P.S.Eu tentei essa abordagem no Django 1.9

Estou usando Django versão 1.9.4

Eu segui os seguintes passos: -

Acabei de renomear o modelo Oldname para o newName Run python manage.py makemigrations.Ele vai te pedirDid you rename the appname.oldName model to NewName? [y/N] selecione Y

Correr python manage.py migrate e ele vai te pedir

Os seguintes tipos de conteúdo estão obsoletos e precisam ser excluídos:

appname | oldName
appname | NewName

Quaisquer objetos relacionados a esses tipos de conteúdo por uma chave estrangeira também serão excluídos.Tem certeza de que deseja excluir esses tipos de conteúdo?Se não tiver certeza, responda 'não'.

Type 'yes' to continue, or 'no' to cancel: Select No

Ele renomeia e migra todos os dados existentes para uma nova tabela nomeada para mim.

Infelizmente, encontrei problemas (cada Django 1.x) com a migração de renomeação que deixa nomes de tabelas antigas no banco de dados.

Django nem tenta nada na mesa antiga, apenas renomeia seu próprio modelo.O mesmo problema com chaves estrangeiras e índices em geral - as alterações não são rastreadas corretamente pelo Django.

A solução mais simples (solução alternativa):

class Foo(models.Model):
     name = models.CharField(unique=True, max_length=32)
     ...
Bar = Foo  # and use Bar only

A solução real (uma maneira fácil de mudar todos os índices, restrições, gatilhos, nomes, etc. em 2 compromissos, mas sim para menor tabelas):

cometer A:

  1. crie o mesmo modelo como o antigo
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. mudar o código para funcionar com o novo modelo Bar apenas.(incluindo todas as relações no esquema)

Na migração prepare-se RunPython, que copiam dados de foo para bar (incluindo id de Foo)

  1. otimização opcional (se necessário para tabelas maiores)

cometer B: (sem pressa, faça isso quando uma equipe inteira for migrada)

  1. queda segura do modelo antigo Foo

limpeza adicional:

  • squash nas migrações

bug no Django:

Eu precisava renomear algumas tabelas.Mas apenas uma renomeação de modelo foi notada pelo Django.Isso aconteceu porque o Django itera sobre modelos adicionados e depois removidos.Para cada par verifica se eles são do mesmo aplicativo e têm campos idênticos.Apenas uma tabela não tinha chaves estrangeiras para tabelas a serem renomeadas (as chaves estrangeiras contêm o nome da classe do modelo, como você se lembra).Ou seja, apenas uma tabela não teve alterações de campo.É por isso que foi notado.

Então, a solução é renomear uma tabela por vez, alterando o nome da classe do modelo em models.py, possivelmente views.py, e fazendo uma migração.Depois disso, inspecione seu código em busca de outras referências (nomes de classes de modelo, nomes relacionados (consulta), nomes de variáveis).Faça uma migração, se necessário.Em seguida, combine opcionalmente todas essas migrações em uma (certifique-se de copiar também as importações).

Eu faria palavras de @ceasaro, minhas em seu comentário sobre isso responder.

Versões mais recentes do Django podem detectar alterações e perguntar o que foi feito.Eu também acrescentaria que o Django pode misturar a ordem de execução de alguns comandos de migração.

Seria sensato aplicar pequenas alterações e executar makemigrations e migrate e se o erro ocorrer, o arquivo de migração poderá ser editado.

A ordem de execução de algumas linhas pode ser alterada para evitar erros.

Só queria confirmar e acrescentar o comentário do Ceasaro.O Django 2.0 parece fazer isso automaticamente agora.

Estou no Jango 2.2.1, tudo o que tive que fazer foi renomear o modelo e executar makemigrations.

Aqui ele pergunta se eu renomeei a classe específica de A para B, escolhi sim e executei a migração e tudo parece funcionar.

Observe que não renomeei o nome do modelo antigo em nenhum arquivo dentro da pasta project/migrations.

Se você estiver usando um bom IDE como o PyCharm, você pode clicar com o botão direito no nome do modelo e refatorar -> renomear.Isso evita o trabalho de passar por todo o código que faz referência ao modelo.Em seguida, execute makemigrations e migre.Django 2+ simplesmente confirmará a mudança de nome.

Atualizei o Django da versão 10 para a versão 11:

sudo pip install -U Django

(-U para "upgrade") e resolveu o problema.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top