Metadata-Version: 2.4
Name: djhero
Version: 0.2.0
Summary: Powerful decorators & utilities for Django developers. Rate limiting, caching, auth, CORS, pagination, retry & more in one package.
Author-email: pyaidev <mashrapov3030@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/pyaidev/djhero
Project-URL: Documentation, https://github.com/pyaidev/djhero#readme
Project-URL: Repository, https://github.com/pyaidev/djhero
Project-URL: Issues, https://github.com/pyaidev/djhero/issues
Keywords: django,decorators,rate-limit,cache,auth,timeout
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.0
Classifier: Framework :: Django :: 4.1
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.0
Dynamic: license-file

# 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

```bash
pip install djhero
```

## Quick Start

```python
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.
```python
@cache_view(ttl=60)
def my_view(request):
    ...
```

#### `@cache_per_user(ttl=300)`
Cache responses per authenticated user.
```python
@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.
```python
@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.
```python
@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.
```python
@auth_required()
@auth_required(login_url="/login/", message="Please log in")
```

#### `@permission_required(*perms, message=None)`
```python
@permission_required("app.can_edit", "app.can_delete")
```

#### `@staff_required`
```python
@staff_required
def admin_panel(request):
    ...
```

#### `@superuser_required`
```python
@superuser_required
def danger_zone(request):
    ...
```

### Request Handling

#### `@json_response`
Automatically convert dict/list returns to JsonResponse.
```python
@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`.
```python
@validate_json("name", "email")
def create(request):
    data = request.json_data  # already parsed & validated
```

#### `@require_fields(*fields, source="POST")`
```python
@require_fields("username", "password", source="POST")
@require_fields("q", source="GET")
```

#### `@handle_exceptions`
Catch exceptions and return JSON error response.
```python
@handle_exceptions
@handle_exceptions(log_errors=True, default_status=500)
```

#### `@timeout(seconds=30, message=None)`
```python
@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.
```python
@log_request
@log_request(level=logging.DEBUG, log_body=True)
```

#### `@ajax_required`
```python
@ajax_required
def api_endpoint(request):
    ...
```

#### `@method_required(*methods)`
```python
@method_required("GET", "POST")
```

#### `@disable_csrf`
```python
@disable_csrf
def webhook(request):
    ...
```

### HTTP Features

#### `@cors(origins, methods, headers, max_age)`
```python
@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`.
```python
@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.
```python
@etag(lambda req: hashlib.md5(str(req.GET).encode()).hexdigest())
def my_view(request):
    ...
```

#### `@deprecated(message, sunset_date)`
Adds `Deprecation` and `Sunset` headers.
```python
@deprecated("Use /api/v2/users instead", sunset_date="2025-12-31")
```

#### `@retry_on_error(max_retries=3, delay=0.5, exceptions=(Exception,))`
```python
@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.
```python
@query_debugger
@query_debugger(warn_threshold=5)
```

---

## Utilities

```python
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:

```python
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:

```python
@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
