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_NUMBER
im 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:
- 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.
- Holen Sie sich die neueste Migration für jede App .
- Führen Sie Migrationen durch.
- 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)