Wie kann die letzte Migration zurückgesetzt werden?

446

Ich habe eine Migration durchgeführt, die eine neue Tabelle hinzugefügt hat, und möchte sie zurücksetzen und die Migration löschen, ohne eine neue Migration zu erstellen.

Wie mache ich es? Gibt es einen Befehl zum Zurücksetzen der letzten Migration und dann kann ich die Migrationsdatei einfach löschen?

Ronen Ness
quelle

Antworten:

794

Sie können zurückkehren, indem Sie zur vorherigen Migration migrieren.

Zum Beispiel, wenn Ihre letzten beiden Migrationen sind:

  • 0010_previous_migration
  • 0011_migration_to_revert

Dann würden Sie tun:

./manage.py migrate my_app 0010_previous_migration 

Sie können dann die Migration löschen 0011_migration_to_revert .

Wenn Sie Django 1.8+ verwenden, können Sie die Namen aller Migrationen mit anzeigen

./manage.py showmigrations my_app

Um alle Migrationen für eine App rückgängig zu machen, können Sie Folgendes ausführen:

./manage.py migrate my_app zero
Alasdair
quelle
7
Ich habe auf SO viele Antworten auf dieses Problem gesehen, die alt sind und einfach nicht mehr funktionieren. +1, da dies mit Django 1.8 funktioniert.
AlanSE
2
Wie wäre es, wenn die App nur eine Migrationsdatei / Erstmigration hat? und ich muss diese anfängliche Migration rückgängig machen?
Adiyat Mubarak
37
Mit dem migrateBefehl können Sie ./manage.py migrate my_app zeroalle Migrationen für die App aufheben.
Alasdair
4
Aus irgendeinem Grund hat ./manage.py migrate my_app 0010_previous_migration bei mir nicht funktioniert. Also habe ich versucht, mit ./manage.py my_app 0010 zu migrieren und es hat funktioniert. Gibt es Gründe, warum Django 1.8 nicht den gesamten Migrationsnamen übernimmt?
Varun Verma
4
Solange Sie Ihren tatsächlichen Migrationsnamen verwenden und nicht '0010_previous_migration', weiß ich nicht, warum Sie dieses Verhalten sehen würden.
Alasdair
36

Die Antwort von Alasdair deckt die Grundlagen ab

  • Identifizieren Sie die gewünschten Migrationen ./manage.py showmigrations
  • migrate Verwenden des App-Namens und des Migrationsnamens

Es sollte jedoch darauf hingewiesen werden, dass nicht alle Migrationen dies können rückgängig gemacht werden. Dies geschieht, wenn Django keine Regel für die Umkehrung hat. Bei den meisten Änderungen, mit denen Sie automatisch Migrationen vorgenommen haben ./manage.py makemigrations, ist die Stornierung möglich. Bei benutzerdefinierten Skripten muss jedoch sowohl vorwärts als auch rückwärts geschrieben werden, wie im folgenden Beispiel beschrieben:

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

So führen Sie eine No-Op-Umkehrung durch

Wenn Sie eine RunPythonOperation hatten, möchten Sie die Migration möglicherweise nur zurücksetzen, ohne ein logisch strenges Umkehrskript zu schreiben. Der folgende schnelle Hack zum Beispiel aus den Dokumenten (obiger Link) ermöglicht dies, sodass die Datenbank auch nach dem Umkehren der Migration im selben Zustand bleibt wie nach der Anwendung der Migration.

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

Dies funktioniert für Django 1.8, 1.9


Update: Ein besserer Weg , dies zu schreiben , wäre zu ersetzen lambda apps, schema_editor: Nonemit migrations.RunPython.noopim Snippet oben. Beides ist funktional dasselbe. (Dank an die Kommentare)

AlanSE
quelle
5
Ab Django 1.8 sollten Sie RunPython.noopanstelle eines Inline-Lambda oder eines gleichwertigen
SpoonMeiser
@SpoonMeiser In der Syntax des Beispiels denke ich, dass das so aussieht migrations.RunPython(forwards_func, migrations.RunPython.noop). Müssen das funktional überprüfen. Das sollte irgendwann als Antwort oder Bearbeitung hinzugefügt werden.
AlanSE
13

Hier ist meine Lösung, da die obige Lösung den Anwendungsfall nicht wirklich abdeckt, wenn Sie verwenden RunPython.

Sie können über das ORM mit auf die Tabelle zugreifen

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

So können Sie die Tabellen abfragen und die für Sie relevanten Einträge löschen. Auf diese Weise können Sie im Detail ändern. Bei RynPythonMigrationen müssen Sie sich auch um die Daten kümmern, die hinzugefügt / geändert / entfernt wurden. Das obige Beispiel zeigt nur, wie Sie über Djang ORM auf die Tabelle zugreifen.

Özer S.
quelle
Wenn Sie mit ForeignKeys neue Modelle mit mehreren Migrationen erstellen, dann erkennen, dass alles falsch ist, und 2-3 Migrationen rückwärts neu starten, mit neuen Modellen, aber manchmal denselben Modellnamen oder denselben Beziehungsnamen ... ist diese Lösung eindeutig der Gewinner. Ich hatte Fehlermeldung wie django.db.utils.ProgrammingError: relation "<relation name>" already existsso ich gemacht , migrate --fakedie falsch ist, so habe ich versucht, zurück zu gehen , dann habe ich psycopg2.ProgrammingError: relation "<other <relation name>" does not existDANK
onekiloparsec
10

Sie können auch die manuell erstellte Tabelle löschen.

Außerdem müssen Sie diese bestimmte Migrationsdatei löschen. Außerdem müssen Sie diesen bestimmten Eintrag in der Django-Migrations-Tabelle löschen (wahrscheinlich den letzten in Ihrem Fall), der mit dieser bestimmten Migration korreliert.

sprksh
quelle
Seien Sie in diesem Fall vorsichtig - Sie müssen überprüfen, ob die Datenbank angemessen ist.
Sławomir Lenart
4
Ich würde sehr vorsichtig hinzufügen. In Postgres können viele Dinge kaputt gehen, zum Beispiel Einschränkungen.
Joedborg
9

Löschen Sie die Migrationsdatei erst nach der Umkehrung. Ich habe diesen Fehler gemacht und ohne die Migrationsdatei wusste die Datenbank nicht, welche Dinge entfernt werden sollten.

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

Löschen Sie die Migrationsdatei. Sobald die gewünschte Migration in Ihren Modellen ist ...

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

Ich habe dies in 1.9.1 getan (um die letzte oder letzte erstellte Migration zu löschen):

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

    Beispiel: rm myapp/migrations/0011*

  2. in der Datenbank angemeldet und diese SQL ausgeführt (in diesem Beispiel postgres)

    delete from django_migrations where name like '0011%';

Ich konnte dann neue Migrationen erstellen, die mit der Migrationsnummer begannen, die ich gerade gelöscht hatte (in diesem Fall 11).

MIkee
quelle
1
+1 Obwohl dies funktioniert, müssen Sie diesen Weg als letzten Ausweg speichern. Außerdem müssen Sie daran denken, Spalten / Tabellen zu bearbeiten / löschen, zu denen die problematische Migration beigetragen hat.
Nehem
Guter Punkt - Ich habe dies verwendet, als ich eine Migration erstellt habe, aber noch nicht "./manage.py migrate" ausgeführt habe
MIkee
3

Diese Antwort gilt für ähnliche Fälle, wenn die Top-Antwort von Alasdair nicht hilft . (ZB wenn die unerwünschte Migration bei jeder neuen Migration bald wieder erstellt wird oder wenn es sich um eine größere Migration handelt, die nicht zurückgesetzt werden kann oder die Tabelle manuell entfernt wurde.)

... die Migration löschen, ohne eine neue Migration zu erstellen?

TL; DR : Sie können einige zuletzt zurückgesetzte (verwirrte) Migrationen löschen und nach dem Reparieren von Modellen eine neue erstellen . Sie können es auch mit anderen Methoden konfigurieren, um keine Tabelle mit dem Befehl migrate zu erstellen . Die letzte Migration muss so erstellt werden, dass sie mit den aktuellen Modellen übereinstimmt .


Fälle, in denen niemand eine Tabelle für ein Modell erstellen möchte, das vorhanden sein muss:

A) Eine solche Tabelle sollte in keiner Datenbank auf keinem Computer und unter keinen Bedingungen vorhanden sein

  • Wann: Es handelt sich um ein Basismodell, das nur für die Modellvererbung eines anderen Modells erstellt wurde.
  • Lösung: Einstellenclass Meta: abstract = True

B) Die Tabelle wird selten, von etwas anderem oder manuell auf besondere Weise erstellt.

  • Lösung: Verwendung class Meta: managed = False
    Die Migration wird nur in Tests erstellt, aber nie verwendet. Die Migrationsdatei ist wichtig, da sonst Datenbanktests ab dem reproduzierbaren Ausgangszustand nicht ausgeführt werden können.

C) Die Tabelle wird nur auf einigen Maschinen verwendet (z. B. in der Entwicklung).

  • Lösung: Verschieben Sie das Modell in eine neue Anwendung, die INSTALLED_APPS nur unter besonderen Bedingungen hinzugefügt wird, oder verwenden Sie eine Bedingung class Meta: managed = some_switch.

D) Das Projekt verwendet mehrere Datenbanken insettings.DATABASES

  • Lösung: Schreiben Sie einen Datenbankrouter mit Methode, allow_migrateum die Datenbanken zu unterscheiden, in denen die Tabelle erstellt werden soll und wo nicht.

Die Migration wird in allen Fällen A), B), C), D) mit Django 1.9+ (und nur in den Fällen B, C, D mit Django 1.8) erstellt, jedoch nur in geeigneten Fällen oder möglicherweise nie auf die Datenbank angewendet erforderlich. Seit Django 1.8 sind Migrationen zum Ausführen von Tests erforderlich. Der vollständige relevante aktuelle Status wird durch Migrationen auch für Modelle mit verwaltetem = Falsch in Django 1.9+ aufgezeichnet, um einen ForeignKey zwischen verwalteten / nicht verwalteten Modellen erstellen zu können oder um das Modell später als verwaltet = wahr zu machen. (Diese Frage wurde zum Zeitpunkt von Django 1.8 geschrieben. Alles hier sollte für Versionen zwischen 1.8 und dem aktuellen 2.2 gültig sein.)

Wenn die letzte Migration nicht leicht rückgängig zu machen ist (sind), ist es möglich, vorsichtig (nach der Datenbanksicherung) eine gefälschte Wiederherstellung durchzuführen ./manage.py migrate --fake my_app 0010_previous_migration durchzuführen und die Tabelle manuell zu löschen.

Erstellen Sie bei Bedarf eine feste Migration aus dem festen Modell und wenden Sie sie an, ohne die Datenbankstruktur zu ändern ./manage.py migrate --fake my_app 0011_fixed_migration.

Hynekcer
quelle
3

Wenn Sie beim Zurücksetzen der Migration auf Probleme stoßen und diese irgendwie durcheinander gebracht haben, können Sie fakeMigrationen durchführen.

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

Für die Django-Version <1.7 wird ein Eintrag in erstelltsouth_migrationhistory Tabelle erstellt. Sie müssen diesen Eintrag löschen.

Jetzt können Sie die Migration problemlos zurücksetzen.

PS: Ich war viel Zeit festgefahren und es hat mir geholfen, eine gefälschte Migration durchzuführen und dann zurück zu kehren.

Pransh Tiwari
quelle
1
Diese Antwort ist für Django <1,7.
Hynekcer