Using Django Ratelimit¶
Use as a decorator¶
Changed in version 4.0.
Import:
from django_ratelimit.decorators import ratelimit
-
@
ratelimit
(group=None, key=, rate=None, method=ALL, block=True)¶ Parameters: - group – None A group of rate limits to count together. Defaults to the dotted name of the view.
- key – What key to use, see Keys.
- rate –
‘5/m’ The number of requests per unit time allowed. Valid units are:
s
- secondsm
- minutesh
- hoursd
- days
Also accepts callables. See Rates. A rate of
0/s
disallows all requests. A rate ofNone
means “no limit” and will allow all requests. - method – ALL Which HTTP method(s) to rate-limit. May be a string, a
list/tuple of strings, or the special values for
ALL
orUNSAFE
(which includesPOST
,PUT
,DELETE
andPATCH
). - block – True Whether to block the request instead of annotating.
HTTP Methods¶
Each decorator can be limited to one or more HTTP methods. The
method=
argument accepts a method name (e.g. 'GET'
) or a list or
tuple of strings (e.g. ('GET', 'OPTIONS')
).
There are two special shortcuts values, both accessible from the
ratelimit
decorator or the is_ratelimited
helper, as well as on
the root ratelimit
module:
from django_ratelimit.decorators import ratelimit
@ratelimit(key='ip', method=ratelimit.ALL)
@ratelimit(key='ip', method=ratelimit.UNSAFE)
def myview(request):
pass
ratelimit.ALL
applies to all HTTP methods. ratelimit.UNSAFE
is a shortcut for ('POST', 'PUT', 'PATCH', 'DELETE')
.
Examples¶
@ratelimit(key='ip', rate='5/m', block=False)
def myview(request):
# Will be true if the same IP makes more than 5 POST
# requests/minute.
was_limited = getattr(request, 'limited', False)
return HttpResponse()
@ratelimit(key='ip', rate='5/m', block=True)
def myview(request):
# If the same IP makes >5 reqs/min, will raise Ratelimited
return HttpResponse()
@ratelimit(key='post:username', rate='5/m',
method=['GET', 'POST'], block=False)
def login(request):
# If the same username is used >5 times/min, this will be True.
# The `username` value will come from GET or POST, determined by the
# request method.
was_limited = getattr(request, 'limited', False)
return HttpResponse()
@ratelimit(key='post:username', rate='5/m')
@ratelimit(key='post:tenant', rate='5/m')
def login(request):
# Use multiple keys by stacking decorators.
return HttpResponse()
@ratelimit(key='get:q', rate='5/m')
@ratelimit(key='post:q', rate='5/m')
def search(request):
# These two decorators combine to form one rate limit: the same search
# query can only be tried 5 times a minute, regardless of the request
# method (GET or POST)
return HttpResponse()
@ratelimit(key='ip', rate='4/h')
def slow(request):
# Allow 4 reqs/hour.
return HttpResponse()
get_rate = lambda g, r: None if r.user.is_authenticated else '100/h'
@ratelimit(key='ip', rate=get_rate)
def skipif1(request):
# Only rate limit anonymous requests
return HttpResponse()
@ratelimit(key='user_or_ip', rate='10/s')
@ratelimit(key='user_or_ip', rate='100/m')
def burst_limit(request):
# Implement a separate burst limit.
return HttpResponse()
@ratelimit(group='expensive', key='user_or_ip', rate='10/h')
def expensive_view_a(request):
return something_expensive()
@ratelimit(group='expensive', key='user_or_ip', rate='10/h')
def expensive_view_b(request):
# Shares a counter with expensive_view_a
return something_else_expensive()
@ratelimit(key='header:x-cluster-client-ip')
def post(request):
# Uses the X-Cluster-Client-IP header value.
return HttpResponse()
@ratelimit(key=lambda g, r: r.META.get('HTTP_X_CLUSTER_CLIENT_IP',
r.META['REMOTE_ADDR'])
def myview(request):
# Use `X-Cluster-Client-IP` but fall back to REMOTE_ADDR.
return HttpResponse()
Class-Based Views¶
New in version 0.5.
Changed in version 3.0.
To use the @ratelimit
decorator with class-based views, use the
Django @method_decorator
:
from django.utils.decorators import method_decorator
from django.views.generic import View
class MyView(View):
@method_decorator(ratelimit(key='ip', rate='1/m', method='GET'))
def get(self, request):
pass
@method_decorator(ratelimit(key='ip', rate='1/m', method='GET'), name='get')
class MyOtherView(View):
def get(self, request):
pass
It is also possible to wrap a whole view later, e.g.:
from django.urls import path
from myapp.views import MyView
from django_ratelimit.decorators import ratelimit
urlpatterns = [
path('/', ratelimit(key='ip', method='GET', rate='1/m')(MyView.as_view())),
]
Warning
Make sure the method
argument matches the method decorated.
Note
Unless given an explicit group
argument, different methods of a
class-based view will be limited separately.
Core Methods¶
New in version 3.0.
In some cases the decorator is not flexible enough to, e.g.,
conditionally apply rate limits. In these cases, you can access the core
functionality in ratelimit.core
. The two major methods are
get_usage
and is_ratelimited
.
from django_ratelimit.core import get_usage, is_ratelimited
-
get_usage
(request, group=None, fn=None, key=None, rate=None, method=ALL, increment=False)¶ Parameters: - request – None The HTTPRequest object.
- group – None A group of rate limits to count together. Defaults to the dotted name of the view.
- fn – None A view function which can be used to calculate the group as if it was decorated by @ratelimit.
- key – What key to use, see Keys.
- rate –
‘5/m’ The number of requests per unit time allowed. Valid units are:
s
- secondsm
- minutesh
- hoursd
- days
Also accepts callables. See Rates.
- method – ALL Which HTTP method(s) to rate-limit. May be a string, a
list/tuple, or
None
for all methods. - increment – False Whether to increment the count or just check.
Returns dict or None: Either returns None, indicating that ratelimiting was not active for this request (for some reason) or returns a dict including the current count, limit, time left in the window, and whether this request should be limited.
-
is_ratelimited
(request, group=None, fn=None, key=None, rate=None, method=ALL, increment=False)¶ Parameters: - request – None The HTTPRequest object.
- group – None A group of rate limits to count together. Defaults to the dotted name of the view.
- fn – None A view function which can be used to calculate the group as if it was decorated by @ratelimit.
- key – What key to use, see Keys.
- rate –
‘5/m’ The number of requests per unit time allowed. Valid units are:
s
- secondsm
- minutesh
- hoursd
- days
Also accepts callables. See Rates.
- method – ALL Which HTTP method(s) to rate-limit. May be a string, a
list/tuple, or
None
for all methods. - increment – False Whether to increment the count or just check.
Returns bool: Whether this request should be limited or not.
is_ratelimited
is a thin wrapper around get_usage
that is
maintained for compatibility. It provides strictly less information.
Warning
get_usage
and is_ratelimited
require either group=
or
fn=
to be passed, or they cannot determine the rate limiting
state and will throw.
Exceptions¶
-
class
ratelimit.exceptions.
Ratelimited
¶ If a request is ratelimited and
block
is set toTrue
, Ratelimit will raiseratelimit.exceptions.Ratelimited
.This is a subclass of Django’s
PermissionDenied
exception, so if you don’t need any special handling beyond the built-in 403 processing, you don’t have to do anything.If you are setting
handler403
in your root URLconf, you can catch this exception in your custom view to return a different response, for example:def handler403(request, exception=None): if isinstance(exception, Ratelimited): return HttpResponse('Sorry you are blocked', status=429) return HttpResponseForbidden('Forbidden')
Middleware¶
There is optional middleware to use a custom view to handle Ratelimited
exceptions.
To use it, add ratelimit.middleware.RatelimitMiddleware
to your
MIDDLEWARE
(toward the bottom of the list) and set
RATELIMIT_VIEW
to the full path of a view you want to use.
The view specified in RATELIMIT_VIEW
will get two arguments, the
request
object (after ratelimit processing) and the exception.