[FIXED] Django-Migration führt bei einem Fehler mehrere Migrationen zurück

Ausgabe

Django-Migrationen haben ein hervorragendes Verhalten in Bezug auf einzelne Migrationen, bei denen, vorausgesetzt, Sie verlassen atomic=True, eine Migration alles oder nichts ist: Sie wird entweder bis zum Abschluss ausgeführt oder alles rückgängig gemacht.

Gibt es eine Möglichkeit, dieses Alles-oder-Nichts-Verhalten für mehrere Migrationen zu erhalten? Das heißt, gibt es eine Möglichkeit, entweder mehrere Migrationen innerhalb einer einschließenden Transaktion auszuführen (was zugegebenermaßen andere Probleme verursachen könnte ) oder alle erfolgreichen Migrationen bei einem Fehler rückgängig zu machen?

Für den Kontext suche ich nach einem einzelnen Befehl oder einer Einstellung, um dies zu tun, damit ich ihn in ein Bereitstellungsskript aufnehmen kann. Derzeit sind Datenbankänderungen der einzige Teil meiner Bereitstellung, der im Falle eines Fehlers nicht zurückgesetzt wird. Ich weiß, dass dies manuell durchgeführt werden kann, indem es python manage.py migrate APP_NAME MIGRATION_NUMBERim Falle eines Fehlers ausgeführt wird, aber dies erfordert Kenntnisse über die zuletzt ausgeführte Migration für jede App.

Lösung

Dies ist kein Feature in Django (mindestens ab 4.1). Um dies selbst zu tun, muss Ihr Code Folgendes tun:

  1. Rufen Sie eine Liste aktueller Apps ab, die Migrationen aufweisen. Dies kann durch Abrufen der Schnittmenge von Apps aus settings.INSTALLED_APPS und den Apps im MigrationRecorder erfolgen.
  2. Holen Sie sich die neueste Migration für jede App .
  3. Führen Sie Migrationen durch.
  4. Wenn Migrationen fehlschlagen, führen Sie den Rollback-Befehl für die letzte Migration für jede App aus.

Hier ist eine Implementierung davon als benutzerdefinierter Verwaltungsbefehl . Dies erfolgt als untergeordnete Klasse des Befehls „migrate management“, sodass alle Befehlszeilenoptionen von „migrate“ offengelegt werden. Um dies zu verwenden, fügen Sie es in eine Datei namens <app>/management/commands/migrate_or_rollback.py(wo <app>ist eine Ihrer installierten Apps) ein und Sie können es mit aufrufen python manage.py migrate_or_rollback:

from django.db.migrations.recorder import MigrationRecorder
from django.core.management import call_command
from django.conf import settings
from django.core.management.commands import migrate
from pprint import pprint

class Command(migrate.Command):
    def handle(self, *args, **options):
        installed_apps = [i.split(".")[-1] for i in settings.INSTALLED_APPS]
        migration_apps = [
            i["app"] for i in MigrationRecorder.Migration.objects.values("app").distinct()
        ]
        migration_apps = [i for i in migration_apps if i in installed_apps]
        latest_migration_by_app = {
            app: MigrationRecorder.Migration.objects.filter(app=app).latest("id").name
            for app in migration_apps
        }
        try:
            super().handle(*args, **options)
        except Exception:
            print("\n\nMigrating failed; rolling back to last migration state:\n")
            pprint(latest_migration_by_app)
            print()
            for app, latest_migration in latest_migration_by_app.items():
                call_command("migrate", app, latest_migration)
            print("\nRollboack successfull\n")
            raise


Beantwortet von –
Zags


Antwort geprüft von –
Marie Seifert (FixError Admin)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like