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'
    }
}

Reference: https://medium.com/analytics-vidhya/throttling-requests-with-django-rest-framework-for-different-http-methods-3ab0461044c

Answered By: Abhishek Bera

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 😀

Answered By: Omar Gaber El-Sayed

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):
   ....
Answered By: Milad shiri