Como reverter a última migração?

446

Fiz uma migração que adicionou uma nova tabela e deseja revertê-la e excluir a migração, sem criar uma nova migração.

Como eu faço isso? Existe um comando para reverter a última migração e, em seguida, posso simplesmente excluir o arquivo de migração?

Ronen Ness
fonte

Respostas:

794

Você pode reverter migrando para a migração anterior.

Por exemplo, se suas duas últimas migrações forem:

  • 0010_previous_migration
  • 0011_migration_to_revert

Então você faria:

./manage.py migrate my_app 0010_previous_migration 

Você pode excluir a migração 0011_migration_to_revert .

Se você estiver usando o Django 1.8+, poderá mostrar os nomes de todas as migrações com

./manage.py showmigrations my_app

Para reverter todas as migrações para um aplicativo, você pode executar:

./manage.py migrate my_app zero
Alasdair
fonte
7
Eu já vi muitas respostas sobre esse problema que são antigas e simplesmente não funcionam mais. +1 porque isso funciona com o Django 1.8.
precisa saber é o seguinte
2
Como se o aplicativo tiver apenas um arquivo de migração / migração inicial. e preciso desfazer essa migração inicial?
Adiyat Mubarak
37
O migratecomando permite que você ./manage.py migrate my_app zeroaplique todas as migrações para o aplicativo.
Alasdair
4
Por alguma razão, ./manage.py migrate my_app 0010_previous_migration não funcionou para mim. Então, tentei usar ./manage.py migrate my_app 0010 e funcionou. Alguma razão para o Django 1.8 não receber o nome completo da migração?
Varun Verma
4
Enquanto você estiver usando seu nome de migração real, e não '0010_previous_migration', não sei por que você veria esse comportamento.
Alasdair
36

A resposta da Alasdair cobre o básico

  • Identifique as migrações desejadas ./manage.py showmigrations
  • migrate usando o nome do aplicativo e o nome da migração

Mas deve-se ressaltar que nem todas as migrações podem ser revertidas. Isso acontece se o Django não tem uma regra para fazer a reversão. Para a maioria das alterações pelas quais você fez as migrações automaticamente ./manage.py makemigrations, a reversão será possível. No entanto, os scripts personalizados precisarão de gravação direta e reversa, conforme descrito no exemplo aqui:

https://docs.djangoproject.com/en/1.9/ref/migration-operations/

Como fazer uma reversão sem operação

Se você teve uma RunPythonoperação, talvez queira apenas voltar a migração sem escrever um script de reversão logicamente rigoroso. O seguinte hack rápido para o exemplo a partir dos documentos (link acima) permite isso, deixando o banco de dados no mesmo estado em que estava após a aplicação da migração, mesmo depois de revertê-lo.

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

def forwards_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).bulk_create([
        Country(name="USA", code="us"),
        Country(name="France", code="fr"),
    ])

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunPython(forwards_func, lambda apps, schema_editor: None),
    ]

Isso funciona para o Django 1.8, 1.9


Update: A melhor maneira de escrever este seria substituir lambda apps, schema_editor: Nonecom migrations.RunPython.noopno trecho acima. Ambos são funcionalmente a mesma coisa. (crédito aos comentários)

AlanSE
fonte
5
A partir do Django 1.8 em diante, você deve usar em RunPython.noopvez de um lambda inline ou equivalente: docs.djangoproject.com/en/1.8/ref/migration-operations/…
SpoonMeiser 10/16
@SpoonMeiser Na sintaxe do exemplo, acho que parece migrations.RunPython(forwards_func, migrations.RunPython.noop). Precisa verificar isso funcionalmente. Isso deve ser adicionado como resposta ou edição a este em algum momento.
AlanSE
13

Aqui está minha solução, já que a solução acima não cobre realmente o caso de uso quando você o usa RunPython.

Você pode acessar a tabela via ORM com

from django.db.migrations.recorder import MigrationRecorder

>>> MigrationRecorder.Migration.objects.all()
>>> MigrationRecorder.Migration.objects.latest('id')
Out[5]: <Migration: Migration 0050_auto_20170603_1814 for model>
>>> MigrationRecorder.Migration.objects.latest('id').delete()
Out[4]: (1, {u'migrations.Migration': 1})

Assim, você pode consultar as tabelas e excluir as entradas relevantes para você. Dessa forma, você pode modificar em detalhes. Com as RynPythonmigrações, você também precisa cuidar dos dados que foram adicionados / alterados / removidos. O exemplo acima mostra apenas como você acessa a tabela via Djang ORM.

Özer S.
fonte
Ao criar novos modelos com ForeignKeys, com várias migrações, perceber que tudo está errado e reiniciar 2-3 migrações para trás, com novos modelos, mas às vezes nomes de modelos ou nomes de relacionamentos ... essa solução é claramente a vencedora. Eu tive uma mensagem de erro django.db.utils.ProgrammingError: relation "<relation name>" already existsassim, eu fiz um migrate --fakeque está errado, então eu tentei voltar, então eu psycopg2.ProgrammingError: relation "<other <relation name>" does not existOBRIGADO
onekiloparsec 02/02/19
10

A outra coisa que você pode fazer é excluir a tabela criada manualmente.

Junto com isso, você terá que excluir esse arquivo de migração específico. Além disso, você terá que excluir essa entrada específica na tabela django-migrations (provavelmente a última no seu caso) que se correlaciona com essa migração específica.

sprksh
fonte
tenha cuidado neste caso - você é obrigado a verificar se o db é adequado.
Sławomir Lenart
4
Eu acrescentaria MUITO cuidado. Você pode quebrar muitas coisas no Postgres, por exemplo, restrições.
Joedborg 24/05
9

Não exclua o arquivo de migração até depois da reversão. Cometi esse erro e, sem o arquivo de migração, o banco de dados não sabia o que remover.

python manage.py showmigrations
python manage.py migrate {app name from show migrations} {00##_migration file.py}

Exclua o arquivo de migração. Quando a migração desejada estiver em seus modelos ...

python manage.py makemigrations
python manage.py migrate
DanGoodrick
fonte
8

Fiz isso no 1.9.1 (para excluir a última ou mais recente migração criada):

  1. rm <appname>/migrations/<migration #>*

    exemplo: rm myapp/migrations/0011*

  2. logado no banco de dados e executou este SQL (postgres neste exemplo)

    delete from django_migrations where name like '0011%';

Pude criar novas migrações que começaram com o número de migração que eu havia acabado de excluir (neste caso, 11).

MIkee
fonte
1
+1 Embora isso funcione, você precisa salvar desta maneira como último recurso. Você também deve se lembrar de editar / soltar colunas / tabelas das quais a migração problemática contribuiu.
nehem 23/02
ponto positivo - usei isso quando criei uma migração, mas ainda não executei "./manage.py migrate"
MIkee
3

Esta resposta é para casos semelhantes, se a resposta principal da Alasdair não ajudar . (Por exemplo, se a migração indesejada for criada em breve novamente a cada nova migração ou se houver uma migração maior que não possa ser revertida ou se a tabela tiver sido removida manualmente.)

... excluir a migração, sem criar uma nova migração?

TL; DR : você pode excluir algumas das últimas migrações revertidas (confusas) e fazer uma nova após corrigir os modelos . Você também pode usar outros métodos para configurá-lo para não criar uma tabela pelo comando migrate. A última migração deve ser criada para corresponder aos modelos atuais .


Casos em que alguém não deseja criar uma tabela para um Modelo que deve existir:

A) Nenhuma tabela deve existir em nenhum banco de dados em nenhuma máquina e sem condições

  • Quando: É um modelo base criado apenas para a herança de outro modelo.
  • Solução: Definirclass Meta: abstract = True

B) A tabela é criada raramente, por outra coisa ou manualmente de uma maneira especial.

  • Solução: Use class Meta: managed = False
    A migração é criada, mas nunca usada, apenas em testes. O arquivo de migração é importante, caso contrário, os testes do banco de dados não podem ser executados, iniciando a partir do estado inicial reproduzível.

C) A tabela é usada apenas em algumas máquinas (por exemplo, em desenvolvimento).

  • Solução: Mova o modelo para um novo aplicativo adicionado ao INSTALLED_APPS apenas sob condições especiais ou use uma condição class Meta: managed = some_switch.

D) O projeto utiliza vários bancos de dados emsettings.DATABASES

  • Solução: escreva um roteador de banco de dados com o método allow_migratepara diferenciar os bancos de dados onde a tabela deve ser criada e onde não.

A migração é criada em todos os casos A), B), C), D) com Django 1.9+ (e somente nos casos B, C, D com Django 1.8), mas aplicada ao banco de dados apenas nos casos apropriados ou talvez nunca se necessário. Migrações são necessárias para a execução de testes desde o Django 1.8. O estado atual relevante completo é registrado pelas migrações, mesmo para os modelos com managed = False no Django 1.9+, para ser possível criar uma ForeignKey entre modelos gerenciados / não gerenciados ou para tornar o modelo gerenciado = True mais tarde. (Esta pergunta foi escrita na época do Django 1.8. Tudo aqui deve ser válido para versões entre 1.8 e 2.2.)

Se a última migração não for facilmente revertível, é possível com cautela (após o backup do banco de dados) fazer uma reversão falsa ./manage.py migrate --fake my_app 0010_previous_migration , exclua a tabela manualmente.

Se necessário, crie uma migração fixa a partir do modelo fixo e aplique-a sem alterar a estrutura do banco de dados ./manage.py migrate --fake my_app 0011_fixed_migration.

hynekcer
fonte
3

Se você estiver enfrentando problemas ao reverter a migração e, de alguma forma, tiver estragado tudo, poderá realizar fakemigrações.

./manage.py migrate <name> --ignore-ghost-migrations --merge --fake

Para a versão do django <1.7, isso criará uma entrada na south_migrationhistorytabela, você precisará excluir essa entrada.

Agora você poderá reverter a migração facilmente.

PS: Fiquei preso por muito tempo, realizando uma migração falsa e, em seguida, reverter novamente me ajudou.

Pransh Tiwari
fonte
1
Esta resposta é para o Django <1.7.
Hynekcer 25/03/19