Skip to content

Commit f80dcb4

Browse files
committed
Add @throttle_scope function based view decorator
1 parent 385df20 commit f80dcb4

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

docs/api-guide/views.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ The available decorators are:
182182
* `@parser_classes(...)`
183183
* `@authentication_classes(...)`
184184
* `@throttle_classes(...)`
185+
* `@throttle_scope(...)`
185186
* `@permission_classes(...)`
186187
* `@content_negotiation_class(...)`
187188
* `@metadata_class(...)`

rest_framework/decorators.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ def handler(self, *args, **kwargs):
6767
WrappedAPIView.throttle_classes = getattr(func, 'throttle_classes',
6868
APIView.throttle_classes)
6969

70+
WrappedAPIView.throttle_scope = getattr(func, 'throttle_scope',
71+
None)
72+
7073
WrappedAPIView.permission_classes = getattr(func, 'permission_classes',
7174
APIView.permission_classes)
7275

@@ -136,6 +139,14 @@ def decorator(func):
136139
return decorator
137140

138141

142+
def throttle_scope(throttle_scope):
143+
def decorator(func):
144+
_check_decorator_order(func, 'throttle_scope')
145+
func.throttle_scope = throttle_scope
146+
return func
147+
return decorator
148+
149+
139150
def permission_classes(permission_classes):
140151
def decorator(func):
141152
_check_decorator_order(func, 'permission_classes')

tests/test_decorators.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import sys
22

33
import pytest
4-
from django.test import TestCase
4+
from django.test import TestCase, override_settings
55

66
from rest_framework import status
77
from rest_framework.authentication import BasicAuthentication
88
from rest_framework.decorators import (
99
action, api_view, authentication_classes, content_negotiation_class,
1010
metadata_class, parser_classes, permission_classes, renderer_classes,
11-
schema, throttle_classes, versioning_class
11+
schema, throttle_classes, throttle_scope, versioning_class
1212
)
1313
from rest_framework.negotiation import BaseContentNegotiation
1414
from rest_framework.parsers import JSONParser
@@ -153,6 +153,25 @@ def view(request):
153153
response = view(request)
154154
assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS
155155

156+
@override_settings(
157+
REST_FRAMEWORK={
158+
"DEFAULT_THROTTLE_CLASSES": ['rest_framework.throttling.ScopedRateThrottle'],
159+
"DEFAULT_THROTTLE_RATES": {"x": "1/day"},
160+
}
161+
)
162+
def test_throttle_scope(self):
163+
@api_view(['GET'])
164+
@throttle_scope("x")
165+
def view(request):
166+
return Response({})
167+
168+
request = self.factory.get('/')
169+
response = view(request)
170+
assert response.status_code == status.HTTP_200_OK
171+
172+
response = view(request)
173+
assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS
174+
156175
def test_versioning_class(self):
157176
@api_view(["GET"])
158177
@versioning_class(QueryParameterVersioning)

0 commit comments

Comments
 (0)