Throttling policies on specific Django Viewset

Hi everyone!

Here is a small article on some experiments I came across with the development of the new cfptime.org version. Goal: I needed to limit (anonymous) visitors on specific API endpoints (especially when creating/posting new Call For Papers to avoid flooding).

It turns out someone had similar thoughts and his blog post was pretty interested and helped me a lot. You can find the blog post here: https://www.pedaldrivenprogramming.com/2017/05/throttling-django-rest-framwork-viewsets/.

As the author states:

The magic is contained within the ScopedRateThrottle. This class will look for the throttle_scope property on the view, and if found, look up a corresponding key in the DEFAULT_THROTTLE_RATES dictionary.

First, define your different throttles policies:

settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny',),
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.ScopedRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/min',
        'user': '1000/day',
        'cfps.create': '2/day'
    }
}

Then, override the get_throttles() method and add your cfps.create policy when the action create is called! This will allow my users to use the CFP Time API to fetch {CFP, Conference} Details but will limit them in order to avoid flooding. w00t w00t!

views.py

class CFPViewSet(viewsets.ModelViewSet):

    today = datetime.datetime.today()
    queryset = Conference.objects.filter(reviewed=True, cfp_deadline__gte=today).order_by('cfp_deadline').values()
    serializer_class = ConferenceSerializer
    http_method_names = ['get', 'post']

    def get_throttles(self):
        if self.action == 'create':
            self.throttle_scope = 'cfps.create'
        return super().get_throttles()

    @csrf_exempt
    def create(self, request):
        return super().create(request)

And after the second attempt…

curl -X POST "http://127.0.0.1:8000/api/cfps/" [... truncated ...] -D -
HTTP/1.1 429 Too Many Requests
Date: Tue, 14 Jan 2020 20:23:57 GMT
Server: WSGIServer/0.2 CPython/3.7.5
Content-Type: application/json
Retry-After: 86369
Vary: Accept, Cookie, Origin
Allow: GET, POST
X-Frame-Options: SAMEORIGIN
Content-Length: 72

{"detail":"Request was throttled. Expected available in 86369 seconds."}