Django Rest Framework Scope Throttling on function based view
Question:
Wanted to ask if someone knows a way or a workaround to how to set different throttle scopes for different request methods in a function-based view.
For example
@api_view(['GET', 'POST'])
def someFunction(request):
if request.method == 'GET':
# set scope for get requests
elif request.method == 'POST':
# set scope for post requests
I tried looking around, but all answers are for class-based views only. Would appreciate the help, thanks.
Answers:
You can solve this by creating all the custom throttling classes first. Note: Only the throttles are in classes but the views are functions.
class PostAnononymousRateThrottle(throttling.AnonRateThrottle):
scope = 'post_anon'
def allow_request(self, request, view):
if request.method == "GET":
return True
return super().allow_request(request, view)
class GetAnononymousRateThrottle(throttling.AnonRateThrottle):
scope = 'get_anon'
def allow_request(self, request, view):
if request.method == "POST":
return True
return super().allow_request(request, view)
class PostUserRateThrottle(throttling.UserRateThrottle):
scope = 'post_user'
def allow_request(self, request, view):
if request.method == "GET":
return True
return super().allow_request(request, view)
class GetUserRateThrottle(throttling.UserRateThrottle):
scope = 'get_user'
def allow_request(self, request, view):
if request.method == "POST":
return True
return super().allow_request(request, view)
You can choose to eliminate the classes if you are not looking for authentication or method type.
Then you need to import this
from rest_framework.decorators import api_view, throttle_classes
Then you can wrap your function view with throttle_classes decorator with all the permissions created
@api_view(['GET', 'POST'])
@throttle_classes([PostAnononymousRateThrottle, GetAnononymousRateThrottle, PostUserRateThrottle, GetUserRateThrottle])
def someFunction(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
elif request.method == 'GET':
return Response({"message": "Hello, world!"})
Don’t forget to mention the throttle rate in the settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'post_anon': '3/minute',
'get_anon': '1/minute',
'post_user': '2/minute',
'get_user': '2/minute'
}
}
I finally found out a workaround for function based views.
Here is how I implemented it.
As it was explained in previous answers, we need to extend the UserRateThrottle class or AnonRateThrottle class depending on our need.
For my case, I was more interested in throttling requests from users.
from rest_framework.throttling import UserRateThrottle
class CustomThrottle(UserRateThrottle):
scope = 'my_custom_scope'
def allow_request(self, request, view):
if request.method == 'GET':
self.scope = 'get_scope'
self.rate = '2/hour'
return True
return super().allow_request(request, view)
And in the settings:
'DEFAULT_THROTTLE_RATES': {
'my_custom_scope': '3/day'
}
By default, this class will throttle POST requests based on the rate set in the settings file. The thing I added here is altering the scope and rate in case the request method was GET. Without this alteration, some problems may occur because of the default caching used by DRF Throttlers. We need to set the rate and scope inside the CustomThrottle class itself or else the scope affiliated with POST method will be applied on both GET and POST.
Finally, we add the decorator on our function-based view.
from rest_framework import api_view, throttle_classes
import CustomThrottle
@api_view(['GET', 'POST'])
@throttle_classes([CustomThrottle])
def someFunction(request):
if request.method == 'GET':
# set scope for get requests
elif request.method == 'POST':
# set scope for post requests
and that’d be it 😀
This is very simple but hard to find. Import throttle_classes from DRF as follows:
from rest_framework.decorators import api_view, throttle_classes
then use it as follows:
@api_view(['POST'])
@throttle_classes([UserRateThrottle])
def your_view(request):
....
Wanted to ask if someone knows a way or a workaround to how to set different throttle scopes for different request methods in a function-based view.
For example
@api_view(['GET', 'POST'])
def someFunction(request):
if request.method == 'GET':
# set scope for get requests
elif request.method == 'POST':
# set scope for post requests
I tried looking around, but all answers are for class-based views only. Would appreciate the help, thanks.
You can solve this by creating all the custom throttling classes first. Note: Only the throttles are in classes but the views are functions.
class PostAnononymousRateThrottle(throttling.AnonRateThrottle):
scope = 'post_anon'
def allow_request(self, request, view):
if request.method == "GET":
return True
return super().allow_request(request, view)
class GetAnononymousRateThrottle(throttling.AnonRateThrottle):
scope = 'get_anon'
def allow_request(self, request, view):
if request.method == "POST":
return True
return super().allow_request(request, view)
class PostUserRateThrottle(throttling.UserRateThrottle):
scope = 'post_user'
def allow_request(self, request, view):
if request.method == "GET":
return True
return super().allow_request(request, view)
class GetUserRateThrottle(throttling.UserRateThrottle):
scope = 'get_user'
def allow_request(self, request, view):
if request.method == "POST":
return True
return super().allow_request(request, view)
You can choose to eliminate the classes if you are not looking for authentication or method type.
Then you need to import this
from rest_framework.decorators import api_view, throttle_classes
Then you can wrap your function view with throttle_classes decorator with all the permissions created
@api_view(['GET', 'POST'])
@throttle_classes([PostAnononymousRateThrottle, GetAnononymousRateThrottle, PostUserRateThrottle, GetUserRateThrottle])
def someFunction(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
elif request.method == 'GET':
return Response({"message": "Hello, world!"})
Don’t forget to mention the throttle rate in the settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'post_anon': '3/minute',
'get_anon': '1/minute',
'post_user': '2/minute',
'get_user': '2/minute'
}
}
I finally found out a workaround for function based views.
Here is how I implemented it.
As it was explained in previous answers, we need to extend the UserRateThrottle class or AnonRateThrottle class depending on our need.
For my case, I was more interested in throttling requests from users.
from rest_framework.throttling import UserRateThrottle
class CustomThrottle(UserRateThrottle):
scope = 'my_custom_scope'
def allow_request(self, request, view):
if request.method == 'GET':
self.scope = 'get_scope'
self.rate = '2/hour'
return True
return super().allow_request(request, view)
And in the settings:
'DEFAULT_THROTTLE_RATES': {
'my_custom_scope': '3/day'
}
By default, this class will throttle POST requests based on the rate set in the settings file. The thing I added here is altering the scope and rate in case the request method was GET. Without this alteration, some problems may occur because of the default caching used by DRF Throttlers. We need to set the rate and scope inside the CustomThrottle class itself or else the scope affiliated with POST method will be applied on both GET and POST.
Finally, we add the decorator on our function-based view.
from rest_framework import api_view, throttle_classes
import CustomThrottle
@api_view(['GET', 'POST'])
@throttle_classes([CustomThrottle])
def someFunction(request):
if request.method == 'GET':
# set scope for get requests
elif request.method == 'POST':
# set scope for post requests
and that’d be it 😀
This is very simple but hard to find. Import throttle_classes from DRF as follows:
from rest_framework.decorators import api_view, throttle_classes
then use it as follows:
@api_view(['POST'])
@throttle_classes([UserRateThrottle])
def your_view(request):
....