DRF Throttling: Verwendung von „scope“ – Vergleich von ScopedRateThrottle und UserRateThrottle-Vererbung

In DRF gibt es zwei häufig verwendete Muster, wenn man scope-basiertes Throttling einsetzt.

  • Muster A: Eine eigene Klasse erstellen, die von UserRateThrottle (oder SimpleRateThrottle) erbt und dort einen festen scope definiert. Diese Klasse wird dann direkt über throttle_classes an der View verwendet.
  • Muster B: ScopedRateThrottle in DEFAULT_THROTTLE_CLASSES registrieren und in der View lediglich throttle_scope = "login" setzen.

Die Frage „Gibt es letztlich keinen Unterschied?“ lässt sich so beantworten:

Wenn du nur ein einzelnes Rate-Limit auf Basis von „user ID (oder nicht authentifizierter IP) + scope“ brauchst, ist das Ergebnis nahezu identisch. Unterschiede zeigen sich jedoch bei Fehlkonfigurationen (Absicherung), Erweiterbarkeit (mehrere Richtlinien/benutzerdefinierte Kriterien) und der Code-Struktur (settings-zentriert vs. code-zentriert).


Gemeinsamkeit: Beide erzeugen einen Schlüssel aus „scope + (user ID oder IP)“

  • ScopedRateThrottle baut den Schlüssel aus throttle_scope (falls vorhanden) plus user ID bzw. IP und sucht die passende Rate in DEFAULT_THROTTLE_RATES.
  • UserRateThrottle drosselt ebenfalls nach user ID (authentifiziert) bzw. IP (nicht authentifiziert). Für mehrere Richtlinien empfiehlt DRF, die Klasse zu erben und unterschiedliche scope-Werte zu setzen.

Unterschied 1) Wo wird der scope definiert? – View-Attribut vs. Klassen-Attribut

Muster A: scope fest in der Klasse (explizit)

from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class CustomDomainSaveThrottle(UserRateThrottle):
    scope = "custom_domain_save"

class SaveCustomDomainView(APIView):
    throttle_classes = [CustomDomainSaveThrottle]
  • Der scope ist in der Klasse festgelegt.
  • Die View muss lediglich die Klasse referenzieren.
  • Tippfehler im scope-String werden reduziert – der Klassenname dient praktisch als Dokumentation.

Muster B: scope in der View, gemeinsamer Throttle (settings-zentriert)

REST_FRAMEWORK = {
  "DEFAULT_THROTTLE_CLASSES": [
    "rest_framework.throttling.ScopedRateThrottle",
  ],
  "DEFAULT_THROTTLE_RATES": {
    "login": "5/min",
  }
}

from rest_framework.views import APIView

class LoginView(APIView):
    throttle_scope = "login"
  • Der scope wird direkt in der View gesetzt.
  • Der gemeinsame Throttle (ScopedRateThrottle) liest throttle_scope aus und holt die Rate aus den Einstellungen.
  • Der Code bleibt kompakt, und Richtlinien lassen sich zentral in den settings verwalten.

Unterschied 2) Wer stellt die Anwendung sicher? – Globale Registrierung vs. View-spezifisches Anhängen

Punkt zu Muster B (häufig missverstanden)

Selbst wenn ScopedRateThrottle in DEFAULT_THROTTLE_CLASSES registriert ist, greift es nicht bei Views ohne throttle_scope. Eine globale Registrierung throttelt also nicht automatisch alle Endpoints.

Vorteil von Muster A

Ohne globale Einstellungen zu verändern, kann man gezielt bestimmte Views mit dem gewünschten Throttle „ausstatten“. Das ist nützlich, wenn settings nur eingeschränkt angepasst werden dürfen oder nur einzelne Endpoints streng limitiert werden sollen.


Unterschied 3) Erweiterbarkeit: Mehr als ein einzelnes Limit – Muster A ist im Vorteil

Die beiden Muster wirken ähnlich, solange jede View genau einen scope und ein Limit hat. Sobald mehr nötig ist, werden die Unterschiede deutlich.

1) Mehrere Throttles (z. B. Burst + Sustained)

Die DRF-Dokumentation zeigt ein Muster mit mehreren UserRateThrottle-Subklassen für Burst- und Sustained-Limits.

class Burst(UserRateThrottle):
    scope = "burst"

class Sustained(UserRateThrottle):
    scope = "sustained"

class SearchView(APIView):
    throttle_classes = [Burst, Sustained]

Mit reinem ScopedRateThrottle lässt sich nicht elegant „zwei scopes auf einer View“ ausdrücken – dafür braucht man wieder eigene Klassen (oder zusätzliche Logik).

2) Andere Kriterien als user ID/IP

ScopedRateThrottle arbeitet standardmäßig mit user ID bzw. IP. In der Praxis kommen jedoch schnell Anforderungen wie diese hinzu:

  • Login: Limitierung nach IP + Benutzername (z. B. gegen account-targeted Angriffe).
  • Multi-Tenant-Systeme: Limitierung nach tenant_id.
  • API-Keys: Limitierung pro Schlüssel.

Diese Fälle erfordern typischerweise eine Anpassung von get_cache_key() und führen damit zu einer benutzerdefinierten Throttle-Klasse – also zu Muster A.


Zwei Schlüssel, die eine API-Verbindung öffnen

Was sollte man wählen?

Für die meisten „View-spezifischen Rate-Limits“ → Muster B (ScopedRateThrottle)

  • Minimaler Code
  • Zentrale Verwaltung über settings
  • Nur Views mit throttle_scope werden limitiert

Wenn eines der folgenden zutrifft → Muster A (Vererbung/benutzerdefinierte Klasse)

  • Mehrere Limits (z. B. Burst + Sustained) gleichzeitig auf einer View
  • Kriterien jenseits von user ID/IP (z. B. IP + Benutzername, Tenant, API-Key)
  • Globale Einstellungen sollen nicht verändert werden, aber bestimmte Views müssen throttled werden
  • Klassenname soll als „Dokumentation“ dienen

Weiterführender Artikel: