Skip to main content

Powerful decorators & utilities for Django developers. Rate limiting, caching, auth, CORS, pagination, retry & more in one package.

Project description

djhero

Powerful decorators & utilities for Django developers. One package, zero hassle.

Stop installing 5+ different packages for common Django patterns. djhero gives you 24 decorators, 5 utilities, and 5 signals — all production-ready.

Install

pip install djhero

Quick Start

from djhero import (
    rate_limit, cache_view, log_request, auth_required,
    json_response, validate_json, handle_exceptions, cors,
)

@rate_limit("100/h")
@cache_view(ttl=300)
@log_request
@cors()
@handle_exceptions
def product_list(request):
    products = Product.objects.all()
    return JsonResponse({"products": list(products.values())})

@auth_required()
@validate_json("name", "email")
@json_response
def create_user(request):
    data = request.json_data
    user = User.objects.create(name=data["name"], email=data["email"])
    return {"id": user.id, "created": True}, 201

Decorators

Caching

@cache_view(ttl=300, key_prefix="djhero")

Cache view responses automatically.

@cache_view(ttl=60)
def my_view(request):
    ...

@cache_per_user(ttl=300)

Cache responses per authenticated user.

@cache_per_user(ttl=120)
def user_dashboard(request):
    ...

Rate Limiting

@rate_limit(limit, key_func=None, message=None)

Limit requests per time window. Adds X-RateLimit-Limit and X-RateLimit-Remaining headers.

@rate_limit("100/h")    # 100 per hour
@rate_limit("10/m")     # 10 per minute
@rate_limit("1000/d")   # 1000 per day
@rate_limit("5/s")      # 5 per second

# Custom key:
@rate_limit("100/h", key_func=lambda r: r.user.id)

@throttle(rate, key_func=None, message=None)

Enforce minimum interval between requests.

@throttle("1/s")
@throttle("5/m")

Auth & Permissions

@auth_required(login_url=None, message=None)

Require authentication. Returns JSON 401 for AJAX, redirects for browser.

@auth_required()
@auth_required(login_url="/login/", message="Please log in")

@permission_required(*perms, message=None)

@permission_required("app.can_edit", "app.can_delete")

@staff_required

@staff_required
def admin_panel(request):
    ...

@superuser_required

@superuser_required
def danger_zone(request):
    ...

Request Handling

@json_response

Automatically convert dict/list returns to JsonResponse.

@json_response
def my_view(request):
    return {"message": "hello"}        # -> JsonResponse 200
    return {"created": True}, 201      # -> JsonResponse 201
    return [1, 2, 3]                   # -> JsonResponse(safe=False)

@validate_json(*required_fields)

Validate JSON body and attach parsed data to request.json_data.

@validate_json("name", "email")
def create(request):
    data = request.json_data  # already parsed & validated

@require_fields(*fields, source="POST")

@require_fields("username", "password", source="POST")
@require_fields("q", source="GET")

@handle_exceptions

Catch exceptions and return JSON error response.

@handle_exceptions
@handle_exceptions(log_errors=True, default_status=500)

@timeout(seconds=30, message=None)

@timeout(seconds=5)
def slow_view(request):
    ...

@log_request / @log_request(level, log_body)

Log method, path, status, duration, IP. Adds X-Response-Time header.

@log_request
@log_request(level=logging.DEBUG, log_body=True)

@ajax_required

@ajax_required
def api_endpoint(request):
    ...

@method_required(*methods)

@method_required("GET", "POST")

@disable_csrf

@disable_csrf
def webhook(request):
    ...

HTTP Features

@cors(origins, methods, headers, max_age)

@cors()                                          # Allow all
@cors(origins="https://example.com")             # Specific origin
@cors(origins=["https://a.com", "https://b.com"])  # Multiple origins

@paginate(per_page=20, max_per_page=100)

Adds request.page, request.per_page, request.offset.

@paginate(per_page=25)
def list_view(request):
    items = Item.objects.all()[request.offset:request.offset + request.per_page]

@etag(etag_func)

ETag support for conditional GET requests.

@etag(lambda req: hashlib.md5(str(req.GET).encode()).hexdigest())
def my_view(request):
    ...

@deprecated(message, sunset_date)

Adds Deprecation and Sunset headers.

@deprecated("Use /api/v2/users instead", sunset_date="2025-12-31")

@retry_on_error(max_retries=3, delay=0.5, exceptions=(Exception,))

@retry_on_error(max_retries=3, exceptions=(ConnectionError,))
def external_api(request):
    ...

@query_debugger

Log and count DB queries. Adds X-Query-Count and X-Query-Time headers.

@query_debugger
@query_debugger(warn_threshold=5)

Utilities

from djhero import success_response, error_response, paginated_response, get_json_body, get_client_ip

# Standardized responses
return success_response({"user": user_data}, message="Created", status=201)
return error_response("Not found", status=404)
return error_response("Validation failed", errors={"email": "Invalid"}, status=422)

# Paginated queryset response
return paginated_response(User.objects.all(), page=1, per_page=10)

# Parse JSON body safely
data = get_json_body(request, default={})

# Get client IP
ip = get_client_ip(request)

Signals

Listen to djhero events:

from djhero import rate_limit_exceeded, auth_failed, view_timeout, view_exception, deprecated_view_accessed

@receiver(rate_limit_exceeded)
def on_rate_limit(sender, request, limit, ip, **kwargs):
    notify_admin(f"Rate limit hit by {ip}")

@receiver(view_exception)
def on_error(sender, request, exception, **kwargs):
    send_to_sentry(exception)

@receiver(deprecated_view_accessed)
def on_deprecated(sender, request, message, **kwargs):
    log_deprecated_usage(request.path)

Composing Decorators

Stack them freely:

@rate_limit("50/h")
@auth_required()
@cache_view(ttl=120)
@cors()
@log_request
@handle_exceptions
@timeout(seconds=10)
@json_response
def api_view(request):
    return {"data": "fast & safe"}

Requirements

  • Python 3.8+
  • Django 4.0+

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

djhero-0.2.0.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

djhero-0.2.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file djhero-0.2.0.tar.gz.

File metadata

  • Download URL: djhero-0.2.0.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for djhero-0.2.0.tar.gz
Algorithm Hash digest
SHA256 82fb0d2c3f81243b1a526c1410fb6ebc4800179a15dc535c9c224054b0c8a5e7
MD5 6377bf5c6027833125a5c58c8ad5448e
BLAKE2b-256 0bc831796cc690e37d52c3b5fa2b5d7e40b36e0edaf4adeb8e2d04d6e4183c10

See more details on using hashes here.

File details

Details for the file djhero-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: djhero-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for djhero-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e5f304f7e4228ab12e00a7008cf94d89e9dc9a28ec010d42a8b2e71a1cd0dfd8
MD5 8fa62b0954e4441debd456cd39724ee5
BLAKE2b-256 707765750b971bd465ac588014df6fefd3190eb19f229c95df2d3ae3006cbebd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page