← Zurück zum Blog
DjangoMigrationsPostgreSQL25. April 2026· 12 min Lesezeit

Django Migrations: Best Practices für große Datenbanken

Inhalt
  1. Wie Migrations funktionieren
  2. Migrations ohne Downtime
  3. Datenmigrations
  4. Migrations squashen
  5. Rollback-Strategie
  6. Häufige Fallstricke

Bei kleinen Datenbanken ist eine Migration eine Sache von Millisekunden. Bei Tabellen mit Millionen Zeilen und einer laufenden Produktion wird es schnell kritisch. Ich zeige dir die Patterns die ich in datenbankintensiven Projekten gelernt habe.

Wie Migrations funktionieren

# Migration erstellen
python manage.py makemigrations

# Migrations anzeigen
python manage.py showmigrations

# Bestimmte Migration
python manage.py migrate myapp 0005

# SQL einer Migration anzeigen (ohne auszuführen!)
python manage.py sqlmigrate myapp 0005

# Fake: Migration als ausgeführt markieren ohne SQL
python manage.py migrate --fake myapp 0005

Migrations ohne Downtime

Große Schema-Änderungen brauchen eine mehrstufige Strategie:

# SCHRITT 1: Neues Feld als nullable hinzufügen (schnell, kein Lock)
class Migration(migrations.Migration):
    operations = [
        migrations.AddField(
            model_name="order",
            name="new_field",
            field=models.CharField(max_length=100, null=True),  # null=True!
        ),
    ]
# SCHRITT 2: Daten befüllen (Datenmigration, batched)
from django.db import migrations

def fill_new_field(apps, schema_editor):
    Order = apps.get_model("orders", "Order")
    # In Batches verarbeiten, nicht alles auf einmal!
    batch_size = 1000
    qs = Order.objects.filter(new_field__isnull=True)
    while True:
        ids = list(qs.values_list("id", flat=True)[:batch_size])
        if not ids:
            break
        Order.objects.filter(id__in=ids).update(
            new_field=compute_value()
        )

class Migration(migrations.Migration):
    operations = [migrations.RunPython(fill_new_field)]
# SCHRITT 3: NOT NULL Constraint hinzufügen
operations = [
    migrations.AlterField(
        model_name="order", name="new_field",
        field=models.CharField(max_length=100),  # null=True entfernt
    ),
]

Datenmigrations

# Datenmigration erstellen
python manage.py makemigrations --empty myapp

# In der Migration:
from django.db import migrations

def migrate_forward(apps, schema_editor):
    # apps.get_model statt direkten Import!
    User = apps.get_model("auth", "User")
    Profile = apps.get_model("myapp", "Profile")

    for user in User.objects.filter(profile__isnull=True).iterator(chunk_size=500):
        Profile.objects.create(user=user, display_name=user.username)

def migrate_backward(apps, schema_editor):
    Profile = apps.get_model("myapp", "Profile")
    Profile.objects.all().delete()

class Migration(migrations.Migration):
    operations = [
        migrations.RunPython(migrate_forward, migrate_backward),
    ]

Migrations squashen

Viele Migrations verlangsamen den Start. Ab ~50 Migrations lohnt sich Squashing:

# Migrations 0001 bis 0050 zu einer zusammenfassen
python manage.py squashmigrations myapp 0001 0050

# Resultat: 0001_squashed_0050.py

Rollback-Strategie

# Zurück zu einer bestimmten Migration
python manage.py migrate myapp 0004

# Komplett zurück
python manage.py migrate myapp zero

# Immer backward-Funktion implementieren!
def migrate_backward(apps, schema_editor):
    # Diese Funktion MUSS den Zustand vor migrate_forward herstellen
    pass

Häufige Fallstricke

Yevhen Chubchyk
Yevhen Chubchyk
Senior Python / Django Entwickler · Freelancer seit 2016 · 20+ Jahre IT-Erfahrung. Kunden: Siemens Healthineers, Engie, natureOffice u.a.