DRF Throttlingで「scope」を使う2つの手法比較:ScopedRateThrottle vs UserRateThrottleの継承

DRFでscopeベースのスロットリングを設定する際、よく使われる2つのパターンがあります。

  • 手法AUserRateThrottle(またはSimpleRateThrottle)を継承し、scopeを埋め込んだカスタムクラスを作成して、ビューにthrottle_classesで直接付与します。
  • 手法BDEFAULT_THROTTLE_CLASSESScopedRateThrottleを登録し、ビューにはthrottle_scope = "login"だけを書きます。

「結局同じ効果では?」という疑問に答えると、次のとおりです。

単一の速度制限を「ユーザーID(または未認証IP)+ scope」で設定する場合、結果はほぼ同じです。 ただし 適用方法(ミス防止)、拡張性(複数ポリシー/カスタム基準)、コード構造(設定中心 vs コード中心)で差が生じ、選択基準が決まります。


共通点:両方とも「scope + (user id or IP)」でキーを作成します

  • ScopedRateThrottleはビューにthrottle_scopeがある場合、scope + ユーザーID/IP でキーを構成し、DEFAULT_THROTTLE_RATESから該当スコープのレートを取得して適用します。
  • UserRateThrottleも基本的に認証ユーザーは user id、未認証は IP を基準に制限し、複数を使う場合はクラスを継承してscopeを変える形をDRFが公式例として示します。

差異1) 「scopeをどこに書くか」:ビュー属性 vs クラス属性

手法A:クラスにscopeを固定(明示的)

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]
  • scopeはクラスに固定されます。
  • ビューは「このクラスを使う」だけで完了。
  • ビューごとにscope文字列をタイプミスするリスクが減ります(クラス名が実質的にドキュメントになります)。

手法B:ビューにscopeだけを書き、共通throttleが解釈(設定中心)

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"
  • scopeはビューに直接書きます。
  • 共通throttle(ScopedRateThrottle)が「このビューはloginスコープ?」と判断し、設定からrateを取得して適用します。
  • コードが短く、settingsでポリシーを一目で管理しやすいです。

差異2) 「適用を誰が保証するか」:基本登録 vs ビュー別装着

手法Bのポイント(誤解が多い)

DEFAULT_THROTTLE_CLASSESScopedRateThrottleを入れても、throttle_scopeが設定されていないビューには適用されません。 つまり「全体設定に入れたら全APIがブロックされる?」という心配は通常不要です。

逆に手法Aの強み

全体設定を触らずに、特定ビューにだけ望むthrottleを「装着」できます。 (チーム/サービス構成上settingsを大きく変更できない場合や、特定アプリ/エンドポイントだけ強制したいときに便利)。


差異3) 拡張性:単一制限を超えるとAが有利になる

見た目はほぼ同じに見えるのは「1ビューに1scope、1速度制限」までです。 その後は差が出ます。

1) 「バースト + 持続」など 複数throttle を1ビューに掛けたい場合

DRFドキュメントでも複数UserRateThrottleを同時に使うパターン(バースト/サステイン)を例示しています。

class Burst(UserRateThrottle):
    scope = "burst"

class Sustained(UserRateThrottle):
    scope = "sustained"

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

ScopedRateThrottleだけでは「1ビューにscopeを2つ」自然に表現しにくく、結局 クラスが必要 になります(またはカスタム実装)。

2) 「誰を基準に制限するか」が変わる瞬間

ScopedRateThrottleはデフォルトで user id / IP 軸で動きます。 しかし実務ではこうした要件がすぐに出てきます。

  • ログイン:IP + usernameの組み合わせで制限したい(アカウントターゲット攻撃対策)
  • 組織/テナント:tenant_idでまとめて制限したい
  • API Key:キー単位で制限したい

この場合は結局、get_cache_key()などのロジックをカスタマイズする必要があり、手法A(継承/カスタムクラス) へ自然に移行します(DRFでもカスタムthrottleを推奨する場面です)。


API連携を開く2つの鍵

それで何を使えばいい?

ほとんどの「ビュー別レートリミット」なら → 手法B(ScopedRateThrottle)推奨

  • コード最小化
  • settingsでポリシー一括管理
  • ビューにはthrottle_scopeだけ書けば完了
  • scopeのないビューには適用されない

以下のいずれかに該当するなら → 手法A(継承/カスタムクラス)推奨

  • 1ビューにバースト+持続など複数制限を同時に掛けたい
  • 「user/IP」ではなくカスタム基準(username+IP、tenant、API key等)が必要
  • 全体設定を触れにくく、特定ビューにだけ強制装着したい
  • クラス名でポリシーを「ドキュメント化」したい(読む人が意図をすぐ把握)

関連記事: