¿Cómo revertir la última migración?

446

He realizado una migración que agregó una nueva tabla y quiero revertirla y eliminar la migración, sin crear una nueva migración.

¿Cómo lo hago? ¿Hay un comando para revertir la última migración y luego simplemente puedo eliminar el archivo de migración?

Ronen Ness
fuente

Respuestas:

794

Puede revertir migrando a la migración anterior.

Por ejemplo, si sus dos últimas migraciones son:

  • 0010_previous_migration
  • 0011_migration_to_revert

Entonces harías:

./manage.py migrate my_app 0010_previous_migration 

Luego puede eliminar la migración 0011_migration_to_revert.

Si está utilizando Django 1.8+, puede mostrar los nombres de todas las migraciones con

./manage.py showmigrations my_app

Para revertir todas las migraciones de una aplicación, puede ejecutar:

./manage.py migrate my_app zero
Alasdair
fuente
77
He visto muchas respuestas sobre SO a este problema que son viejas y simplemente ya no funcionan. +1 porque esto funciona con Django 1.8.
AlanSE
2
Cómo si la aplicación solo tiene un archivo de migración / migración inicial. y necesito deshacer esa migración inicial?
Adiyat Mubarak
37
El migratecomando le permite usar ./manage.py migrate my_app zeropara no aplicar todas las migraciones de la aplicación.
Alasdair
44
Por alguna razón, ./manage.py migrate my_app 0010_previous_migration no funcionó para mí. Así que intenté usar ./manage.py migrate my_app 0010 y funcionó. ¿Alguna razón por la que Django 1.8 no tomará el nombre completo de la migración?
Varun Verma
44
Mientras esté usando su nombre de migración real, y no '0010_previous_migration', no sé por qué vería ese comportamiento.
Alasdair
36

La respuesta de Alasdair cubre los conceptos básicos.

  • Identifica las migraciones que deseas por ./manage.py showmigrations
  • migrate usando el nombre de la aplicación y el nombre de la migración

Pero debe señalarse que no todas las migraciones pueden revertirse. Esto sucede si Django no tiene una regla para hacer la inversión. Para la mayoría de los cambios por los que realizó migraciones automáticamente ./manage.py makemigrations, será posible la reversión. Sin embargo, las secuencias de comandos personalizadas deberán tener una escritura tanto directa como inversa, como se describe en el ejemplo aquí:

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

Cómo hacer una inversión sin operación

Si tuvo una RunPythonoperación, tal vez solo quiera retroceder la migración sin escribir un guión de inversión lógicamente riguroso. El siguiente truco rápido para el ejemplo de los documentos (enlace anterior) lo permite, dejando la base de datos en el mismo estado que tenía después de que se aplicara la migración, incluso después de revertirla.

# -*- 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),
    ]

Esto funciona para Django 1.8, 1.9


Actualización: Una mejor manera de escribir esta sería la de sustituir lambda apps, schema_editor: Nonecon migrations.RunPython.noopel fragmento anterior. Ambos son funcionalmente la misma cosa. (crédito a los comentarios)

AlanSE
fuente
55
Desde Django 1.8 en adelante, debe usar en RunPython.nooplugar de un lambda en línea o equivalente: docs.djangoproject.com/en/1.8/ref/migration-operations/…
SpoonMeiser
@SpoonMeiser En la sintaxis del ejemplo, creo que se ve así migrations.RunPython(forwards_func, migrations.RunPython.noop). Necesito verificar eso funcionalmente. Eso debería agregarse como respuesta o edición en algún momento.
AlanSE
13

Aquí está mi solución, ya que la solución anterior realmente no cubre el caso de uso, cuando lo usa RunPython.

Puede acceder a la tabla a través del ORM con

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})

Por lo tanto, puede consultar las tablas y eliminar las entradas que sean relevantes para usted. De esta manera puede modificar en detalle. Con las RynPythonmigraciones también debe cuidar los datos que se agregaron / cambiaron / eliminaron. El ejemplo anterior solo muestra cómo accede a la tabla a través de Djang ORM.

Özer S.
fuente
Al crear nuevos modelos con ForeignKeys, con múltiples migraciones, luego darse cuenta de que todo está mal y reiniciar 2-3 migraciones hacia atrás, con nuevos modelos pero a veces los mismos nombres de modelo o los mismos nombres de relación ... esta solución es claramente la ganadora. Tuve un mensaje de error como el django.db.utils.ProgrammingError: relation "<relation name>" already existsque hice, lo migrate --fakecual es incorrecto, así que traté de regresar, luego obtuve psycopg2.ProgrammingError: relation "<other <relation name>" does not existGRACIAS
onekiloparsec
10

La otra cosa que puede hacer es eliminar la tabla creada manualmente.

Junto con eso, tendrá que eliminar ese archivo de migración en particular. Además, tendrá que eliminar esa entrada en particular en la tabla django-migrations (probablemente la última en su caso) que se correlaciona con esa migración en particular.

sprksh
fuente
tenga cuidado en este caso: está obligado a verificar que db sea adecuado.
Sławomir Lenart
44
Yo agregaría MUY cuidado. Podría romper muchas cosas en Postgres, por ejemplo, restricciones.
joedborg
9

No elimine el archivo de migración hasta después de la reversión. Cometí este error y sin el archivo de migración, la base de datos no sabía qué cosas eliminar.

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

Eliminar el archivo de migración. Una vez que la migración deseada esté en sus modelos ...

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

Hice esto en 1.9.1 (para eliminar la última o la última migración creada):

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

    ejemplo: rm myapp/migrations/0011*

  2. inició sesión en la base de datos y ejecutó este SQL (postgres en este ejemplo)

    delete from django_migrations where name like '0011%';

Luego pude crear nuevas migraciones que comenzaron con el número de migración que acababa de eliminar (en este caso, 11).

MIkee
fuente
1
+1 Aunque esto funcionará, debe guardar de esta manera como último recurso. También debe recordar editar / soltar columnas / tablas de las cuales contribuyó la migración problemática.
nehem
buen punto: utilicé esto cuando creé una migración pero aún no ejecuté "./manage.py migrate"
Mikee
3

Esta respuesta es para casos similares si la respuesta principal de Alasdair no ayuda . (Por ejemplo, si la migración no deseada se crea pronto nuevamente con cada nueva migración o si se trata de una migración más grande que no se puede revertir o la tabla se ha eliminado manualmente).

... eliminar la migración, sin crear una nueva migración?

TL; DR : puede eliminar algunas de las últimas migraciones revertidas (confusas) y hacer una nueva después de corregir los modelos . También puede usar otros métodos para configurarlo para que no cree una tabla mediante el comando migrate. La última migración debe crearse para que coincida con los modelos actuales .


Casos por los cuales alguien no quiere crear una tabla para un Modelo que debe existir:

A) No debería existir tal tabla en ninguna base de datos en ninguna máquina y sin condiciones

  • Cuándo: es un modelo base creado solo para la herencia del modelo de otro modelo.
  • Solución: establecerclass Meta: abstract = True

B) La tabla se crea raramente, por otra cosa o manualmente de una manera especial.

  • Solución: uso class Meta: managed = False
    La migración se crea, pero nunca se usa, solo en pruebas. El archivo de migración es importante, de lo contrario las pruebas de la base de datos no pueden ejecutarse, comenzando desde el estado inicial reproducible.

C) La tabla se usa solo en algunas máquinas (por ejemplo, en desarrollo).

  • Solución: mueva el modelo a una nueva aplicación que se agregue a INSTALLED_APPS solo en condiciones especiales o use un condicional class Meta: managed = some_switch.

D) El proyecto utiliza múltiples bases de datos ensettings.DATABASES

  • Solución: escriba un enrutador de base de datos con método allow_migratepara diferenciar las bases de datos donde se debe crear la tabla y dónde no.

La migración se crea en todos los casos A), B), C), D) con Django 1.9+ (y solo en los casos B, C, D con Django 1.8), pero se aplica a la base de datos solo en los casos apropiados o tal vez nunca si requerido así. Las migraciones han sido necesarias para ejecutar pruebas desde Django 1.8. Las migraciones registran el estado actual relevante completo, incluso para modelos con Managed = False en Django 1.9+ para que sea posible crear una ForeignKey entre modelos administrados / no administrados o para que el modelo sea administrado = True más adelante. (Esta pregunta se ha escrito en el momento de Django 1.8. Todo aquí debería ser válido para versiones entre la 1.8 y la 2.2 actual).

Si la última migración no es (son) fácilmente reversibles, entonces es posible realizar con cautela (después de la copia de seguridad de la base de datos) una reversión falsa ./manage.py migrate --fake my_app 0010_previous_migration , eliminar la tabla manualmente.

Si es necesario, cree una migración fija desde el modelo fijo y aplíquela sin cambiar la estructura de la base de datos ./manage.py migrate --fake my_app 0011_fixed_migration.

hynekcer
fuente
3

Si tiene problemas al revertir la migración, y de alguna manera lo ha desordenado, puede realizar fakemigraciones.

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

Para django versión <1.7 esto creará una entrada en la south_migrationhistorytabla, debe eliminar esa entrada.

Ahora podrá revertir la migración fácilmente.

PD: Estuve atrapado durante mucho tiempo y realizar una migración falsa y luego volver atrás me ayudó.

Pransh Tiwari
fuente
1
Esta respuesta es para Django <1.7.
hynekcer