Rate limiting for django using limits.
Documentation: https://andriykohut.github.io/django-ratelimiter/
pip install django-ratelimiter
By default django-ratelimiter
will use the default cache.
To use a non-default cache define DJANGO_RATELIMITER_CACHE
in settings.py
.
# Set up django caches
CACHES = {
"custom-cache": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
}
}
# "default" cache is used if setting is not defined.
DJANGO_RATELIMITER_CACHE = "custom-cache"
Any storage backend provided by limits
package can also be used by defining DJANGO_RATELIMITER_STORAGE
:
from limits.storage import RedisStorage
DJANGO_RATELIMITER_STORAGE = RedisStorage(uri="redis://localhost:6379/0")
For more details on storages refer to limits documentation.
- Fixed window
- Fixed Window with Elastic Expiry
- Moving Window - Only supported with
limits
storage by settingDJANGO_RATELIMITER_STORAGE
By default all requests are rate limited
from django_ratelimiter import ratelimit
@ratelimit("5/minute")
def view(request: HttpRequest) -> HttpResponse:
return HttpResponse("OK")
Pick a rate limiting strategy, default is fixed-window
:
# options: fixed-window, fixed-window-elastic-expiry, moving-window
@ratelimit("5/minute", strategy="fixed-window-elastic-expiry")
def view(request: HttpRequest) -> HttpResponse:
return HttpResponse("OK")
You can define per-user limits using request attribute key.
@ratelimit("5/minute", key="user")
def view(request: HttpRequest) -> HttpResponse:
return HttpResponse("OK")
Callable key can be used to define more complex rules:
@ratelimit("5/minute", key=lambda r: r.user.username)
def view(request: HttpRequest) -> HttpResponse:
return HttpResponse("OK")
Rate-limit only certain methods:
@ratelimit("5/minute", methods=["POST", "PUT"])
def view(request):
return HttpResponse("OK")
Provide a custom response:
from django.http import HttpResponse
@ratelimit("5/minute", response=HttpResponse("Too many requests", status=400))
def view(request):
return HttpResponse("OK")
Using non-default storage:
from limits.storage import RedisStorage
@ratelimit("5/minute", storage=RedisStorage(uri="redis://localhost:6379/0"))
def view(request):
return HttpResponse("OK")
Middleware can be used instead of decorators for more general cases.
from typing import Optional
from django.http import HttpRequest
from django_ratelimiter.middleware import AbstractRateLimiterMiddleware
class RateLimiterMiddleware(AbstractRateLimiterMiddleware):
def rate_for(self, request: HttpRequest) -> Optional[str]:
# allow only 100 POST requests per minute
if request.method == "POST":
return "100/minute"
# allow only 200 PUT requests per minute
if request.methid == "PUT":
return "200/minute"
# all other requests are not rate limited
return None
Middleware is customizable by overriding methods, see api reference for more details.
django-ratelimiter
is framework-agnostic, it should work with DRF/ninja out of the box.
Class-based views are also supported with method_decorator.
See examples in test_app.