2026-02-04 16:49:53 +01:00

1.8 KiB

ANTI-PATTERN: N+1 Queries in Django ORM

KONTEXT

Django/DRF API-Entwicklung, insbesondere bei Listen-Endpoints mit Related Objects.

WAS IST PASSIERT?

# SCHLECHT: N+1 Query Problem
def get_matches(request):
    matches = Match.objects.filter(scenario_id=scenario_id)
    return Response([{
        'id': m.id,
        'home_team': m.home_team.name,  # Query für jedes Match!
        'away_team': m.away_team.name,  # Noch eine Query!
        'venue': m.home_team.venue.name  # Und noch eine!
    } for m in matches])

Bei 100 Matches: 1 + 100 + 100 + 100 = 301 Queries statt 1-4.

WARUM WAR ES SCHLECHT?

  • Performance: Exponentieller Anstieg der DB-Queries mit Datenmenge
  • Latenz: Jede Query hat Overhead (Netzwerk, Parsing, Locking)
  • DB-Last: Unnötige Belastung der Datenbank
  • Skalierung: Funktioniert in Dev (10 Records), bricht in Prod (10.000 Records)

DIE BESSERE ALTERNATIVE

# GUT: Optimierte Queries
def get_matches(request):
    matches = Match.objects.filter(
        scenario_id=scenario_id
    ).select_related(
        'home_team',
        'away_team',
        'home_team__venue',  # Nested relation
    )
    return Response([{
        'id': m.id,
        'home_team': m.home_team.name,
        'away_team': m.away_team.name,
        'venue': m.home_team.venue.name
    } for m in matches])

Bei 100 Matches: 1 Query (mit JOINs).

ERKENNUNGSREGELN

  • select_related() für ForeignKey / OneToOneField
  • prefetch_related() für ManyToMany / Reverse ForeignKey
  • Django Debug Toolbar zeigt Query-Anzahl
  • django-query-counter als Middleware

CHECKLISTE

  • Hat jeder Listen-Endpoint select_related/prefetch_related?
  • Werden verschachtelte Relations berücksichtigt?
  • Ist die Query-Anzahl bei >100 Records akzeptabel?