# django-ratelimiter
[![PyPI version](https://badge.fury.io/py/django-ratelimiter.svg)](https://pypi.org/project/django-ratelimiter/)
[![CI](https://github.com/andriykohut/django-ratelimiter/actions/workflows/ci.yml/badge.svg)](https://github.com/andriykohut/django-ratelimiter/actions/workflows/ci.yml?query=branch%3Amain)
[![codecov](https://codecov.io/gh/andriykohut/django-ratelimiter/branch/main/graph/badge.svg)](https://codecov.io/gh/andriykohut/django-ratelimiter)
[![Python Versions](https://img.shields.io/pypi/pyversions/django-ratelimiter)](https://pypi.python.org/pypi/django-ratelimiter/)
[![license](https://img.shields.io/pypi/l/django-ratelimiter.svg)](https://pypi.python.org/pypi/django-ratelimiter)
[![docs](https://github.com/andriykohut/django-ratelimiter/actions/workflows/pages/pages-build-deployment/badge.svg?branch=gh-pages)](https://andriykohut.github.io/django-ratelimiter/)
Rate limiting for django using [limits](https://limits.readthedocs.io/en/stable/).
Documentation: <https://andriykohut.github.io/django-ratelimiter/>
## Installation
```py
pip install django-ratelimiter
```
## Usage
By default `django-ratelimiter` will use the default cache.
### Django configuration
To use a non-default cache define `DJANGO_RATELIMITER_CACHE` in `settings.py`.
```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`:
```py
from limits.storage import RedisStorage
DJANGO_RATELIMITER_STORAGE = RedisStorage(uri="redis://localhost:6379/0")
```
For more details on storages refer to limits [documentation](https://limits.readthedocs.io/en/stable/storage.html).
### Rate limiting strategies
- [Fixed window](https://limits.readthedocs.io/en/stable/strategies.html#fixed-window)
- [Fixed Window with Elastic Expiry](https://limits.readthedocs.io/en/stable/strategies.html#fixed-window-with-elastic-expiry)
- [Moving Window](https://limits.readthedocs.io/en/stable/strategies.html#moving-window) - Only supported with `limits` storage by setting `DJANGO_RATELIMITER_STORAGE`
### View decorator
By default all requests are rate limited
```py
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`:
```py
# 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.
```py
@ratelimit("5/minute", key="user")
def view(request: HttpRequest) -> HttpResponse:
return HttpResponse("OK")
```
Callable key can be used to define more complex rules:
```py
@ratelimit("5/minute", key=lambda r: r.user.username)
def view(request: HttpRequest) -> HttpResponse:
return HttpResponse("OK")
```
Rate-limit only certain methods:
```py
@ratelimit("5/minute", methods=["POST", "PUT"])
def view(request):
return HttpResponse("OK")
```
Provide a custom response:
```py
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:
```py
from limits.storage import RedisStorage
@ratelimit("5/minute", storage=RedisStorage(uri="redis://localhost:6379/0"))
def view(request):
return HttpResponse("OK")
```
### Middleware
Middleware can be used instead of decorators for more general cases.
```py
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.
### DRF/ninja/class-based views
`django-ratelimiter` is framework-agnostic, it should work with DRF/ninja out of the box.
Class-based views are also supported with [method_decorator](https://docs.djangoproject.com/en/5.0/topics/class-based-views/intro/#decorating-the-class).
See examples in [test_app](./test_app/views.py).
Raw data
{
"_id": null,
"home_page": "https://andriykohut.github.io/django-ratelimiter/",
"name": "django-ratelimiter",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "rate-limit, django",
"author": "Andrii Kohut",
"author_email": "kogut.andriy@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/69/b5/7fdc5529647af06ebaae3b27239c129d246beda1f8b036d2342dcaa4fc76/django_ratelimiter-0.2.1.tar.gz",
"platform": null,
"description": "# django-ratelimiter\n\n[![PyPI version](https://badge.fury.io/py/django-ratelimiter.svg)](https://pypi.org/project/django-ratelimiter/)\n[![CI](https://github.com/andriykohut/django-ratelimiter/actions/workflows/ci.yml/badge.svg)](https://github.com/andriykohut/django-ratelimiter/actions/workflows/ci.yml?query=branch%3Amain)\n[![codecov](https://codecov.io/gh/andriykohut/django-ratelimiter/branch/main/graph/badge.svg)](https://codecov.io/gh/andriykohut/django-ratelimiter)\n[![Python Versions](https://img.shields.io/pypi/pyversions/django-ratelimiter)](https://pypi.python.org/pypi/django-ratelimiter/)\n[![license](https://img.shields.io/pypi/l/django-ratelimiter.svg)](https://pypi.python.org/pypi/django-ratelimiter)\n[![docs](https://github.com/andriykohut/django-ratelimiter/actions/workflows/pages/pages-build-deployment/badge.svg?branch=gh-pages)](https://andriykohut.github.io/django-ratelimiter/)\n\nRate limiting for django using [limits](https://limits.readthedocs.io/en/stable/).\n\nDocumentation: <https://andriykohut.github.io/django-ratelimiter/>\n\n## Installation\n\n```py\npip install django-ratelimiter\n```\n\n## Usage\n\nBy default `django-ratelimiter` will use the default cache.\n\n### Django configuration\n\nTo use a non-default cache define `DJANGO_RATELIMITER_CACHE` in `settings.py`.\n\n```py\n# Set up django caches\nCACHES = {\n \"custom-cache\": {\n \"BACKEND\": \"django.core.cache.backends.redis.RedisCache\",\n \"LOCATION\": \"redis://127.0.0.1:6379\",\n }\n}\n\n# \"default\" cache is used if setting is not defined.\nDJANGO_RATELIMITER_CACHE = \"custom-cache\"\n```\n\nAny storage backend provided by `limits` package can also be used by defining `DJANGO_RATELIMITER_STORAGE`:\n\n```py\nfrom limits.storage import RedisStorage\n\nDJANGO_RATELIMITER_STORAGE = RedisStorage(uri=\"redis://localhost:6379/0\")\n```\n\nFor more details on storages refer to limits [documentation](https://limits.readthedocs.io/en/stable/storage.html).\n\n### Rate limiting strategies\n\n- [Fixed window](https://limits.readthedocs.io/en/stable/strategies.html#fixed-window)\n- [Fixed Window with Elastic Expiry](https://limits.readthedocs.io/en/stable/strategies.html#fixed-window-with-elastic-expiry)\n- [Moving Window](https://limits.readthedocs.io/en/stable/strategies.html#moving-window) - Only supported with `limits` storage by setting `DJANGO_RATELIMITER_STORAGE`\n\n### View decorator\n\nBy default all requests are rate limited\n\n```py\nfrom django_ratelimiter import ratelimit\n\n@ratelimit(\"5/minute\")\ndef view(request: HttpRequest) -> HttpResponse:\n return HttpResponse(\"OK\")\n```\n\nPick a rate limiting strategy, default is `fixed-window`:\n\n```py\n# options: fixed-window, fixed-window-elastic-expiry, moving-window\n@ratelimit(\"5/minute\", strategy=\"fixed-window-elastic-expiry\")\ndef view(request: HttpRequest) -> HttpResponse:\n return HttpResponse(\"OK\")\n```\n\nYou can define per-user limits using request attribute key.\n\n```py\n@ratelimit(\"5/minute\", key=\"user\")\ndef view(request: HttpRequest) -> HttpResponse:\n return HttpResponse(\"OK\")\n```\n\nCallable key can be used to define more complex rules:\n\n```py\n@ratelimit(\"5/minute\", key=lambda r: r.user.username)\ndef view(request: HttpRequest) -> HttpResponse:\n return HttpResponse(\"OK\")\n```\n\nRate-limit only certain methods:\n\n```py\n@ratelimit(\"5/minute\", methods=[\"POST\", \"PUT\"])\ndef view(request):\n return HttpResponse(\"OK\")\n```\n\nProvide a custom response:\n\n```py\nfrom django.http import HttpResponse\n\n@ratelimit(\"5/minute\", response=HttpResponse(\"Too many requests\", status=400))\ndef view(request):\n return HttpResponse(\"OK\")\n```\n\nUsing non-default storage:\n\n```py\n\nfrom limits.storage import RedisStorage\n\n@ratelimit(\"5/minute\", storage=RedisStorage(uri=\"redis://localhost:6379/0\"))\ndef view(request):\n return HttpResponse(\"OK\")\n```\n\n### Middleware\n\nMiddleware can be used instead of decorators for more general cases.\n\n```py\nfrom typing import Optional\n\nfrom django.http import HttpRequest\n\nfrom django_ratelimiter.middleware import AbstractRateLimiterMiddleware\n\n\nclass RateLimiterMiddleware(AbstractRateLimiterMiddleware):\n def rate_for(self, request: HttpRequest) -> Optional[str]:\n # allow only 100 POST requests per minute\n if request.method == \"POST\":\n return \"100/minute\"\n # allow only 200 PUT requests per minute\n if request.methid == \"PUT\":\n return \"200/minute\"\n # all other requests are not rate limited\n return None\n```\n\nMiddleware is customizable by overriding methods, see api reference for more details.\n\n### DRF/ninja/class-based views\n\n`django-ratelimiter` is framework-agnostic, it should work with DRF/ninja out of the box.\nClass-based views are also supported with [method_decorator](https://docs.djangoproject.com/en/5.0/topics/class-based-views/intro/#decorating-the-class).\n\nSee examples in [test_app](./test_app/views.py).\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Rate-limiting for django",
"version": "0.2.1",
"project_urls": {
"Documentation": "https://andriykohut.github.io/django-ratelimiter/",
"Homepage": "https://andriykohut.github.io/django-ratelimiter/",
"Repository": "https://github.com/andriykohut/django-ratelimiter"
},
"split_keywords": [
"rate-limit",
" django"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "85122d17821d84528f96f59c01ac4e366f597cf3bd8976280af900e9c79af5a9",
"md5": "20ad632d97ac52e8131bdbe48a5b5fa6",
"sha256": "13dbed67844a88aa7f4ca43bd956aa5dbf4bf24b066170ca2a2064c6d9426bcb"
},
"downloads": -1,
"filename": "django_ratelimiter-0.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "20ad632d97ac52e8131bdbe48a5b5fa6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 8099,
"upload_time": "2024-04-23T17:03:34",
"upload_time_iso_8601": "2024-04-23T17:03:34.217195Z",
"url": "https://files.pythonhosted.org/packages/85/12/2d17821d84528f96f59c01ac4e366f597cf3bd8976280af900e9c79af5a9/django_ratelimiter-0.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "69b57fdc5529647af06ebaae3b27239c129d246beda1f8b036d2342dcaa4fc76",
"md5": "126be8897ced62507276a4771add0e2c",
"sha256": "b2d47243022b90e233223e193a319a8c9d19376c3322290d4a1e2a958dea47e6"
},
"downloads": -1,
"filename": "django_ratelimiter-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "126be8897ced62507276a4771add0e2c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 6123,
"upload_time": "2024-04-23T17:03:35",
"upload_time_iso_8601": "2024-04-23T17:03:35.979461Z",
"url": "https://files.pythonhosted.org/packages/69/b5/7fdc5529647af06ebaae3b27239c129d246beda1f8b036d2342dcaa4fc76/django_ratelimiter-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-23 17:03:35",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "andriykohut",
"github_project": "django-ratelimiter",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "django-ratelimiter"
}