Metadata-Version: 2.5
Name: uploadkit-django
Version: 0.1.0
Summary: Django integration for UploadKit
Project-URL: Homepage, https://github.com/uploadkit/uploadkit-django
Project-URL: Repository, https://github.com/uploadkit/uploadkit-django
Author: UploadKit
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: django,upload,uploadkit
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
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: Typing :: Typed
Requires-Python: <3.14,>=3.10
Requires-Dist: django>=4.2
Requires-Dist: uploadkit>=0.1.0
Provides-Extra: dev
Requires-Dist: coverage>=7.0; extra == 'dev'
Requires-Dist: pytest-django>=4.8; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: uploadkit-security>=0.1.0; extra == 'dev'
Requires-Dist: uploadkit-testing>=0.1.0; extra == 'dev'
Provides-Extra: security
Requires-Dist: uploadkit-security>=0.1.0; extra == 'security'
Description-Content-Type: text/markdown

# uploadkit-django

[![CI](https://github.com/uploadkit/uploadkit-django/actions/workflows/ci.yml/badge.svg)](https://github.com/uploadkit/uploadkit-django/actions/workflows/ci.yml)
[![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/uploadkit/uploadkit-django/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](pyproject.toml)
[![Django](https://img.shields.io/badge/django-4.2%2B-green)](pyproject.toml)

Django integration for UploadKit.

## What problem does this solve?

Adapts Django uploaded files and maps UploadKit exceptions to HTTP responses — without reimplementing validation or storage.

## When to use it

Use when your Django (or DRF) app uploads files through UploadKit Core.

## When not to use it

Do not put validators, policies, or storage implementations in this package. Supply your own `StorageProvider` (e.g. boto3 → AWS S3 or MinIO).

## Installation

Requires **Python 3.10–3.13** and **Django 4.2+**.

```bash
pip install uploadkit-django uploadkit-security
```

```bash
uv add uploadkit-django uploadkit-security
```

```bash
poetry add uploadkit-django uploadkit-security
```

For S3/MinIO samples: `pip install boto3`.

### Python × Django support

| Python | Django |
|--------|--------|
| 3.10–3.12 | Django 4.2+ |
| 3.13 | Newest Django that declares support for 3.13 (verified in CI) |

Python 3.14 will be added once Django officially supports it.

## Storage provider (AWS S3 or MinIO)

Same class for both backends — omit `endpoint_url` for AWS, set it for MinIO:

```python
# myapp/storage.py
import boto3
from botocore.client import Config
from django.conf import settings


class Boto3S3Storage:
    def __init__(
        self,
        *,
        access_key: str,
        secret_key: str,
        region: str = "us-east-1",
        endpoint_url: str | None = None,
    ) -> None:
        kwargs: dict = {
            "service_name": "s3",
            "aws_access_key_id": access_key,
            "aws_secret_access_key": secret_key,
            "region_name": region,
            "config": Config(signature_version="s3v4"),
        }
        if endpoint_url:
            kwargs["endpoint_url"] = endpoint_url
        self.client = boto3.client(**kwargs)

    def put(self, *, bucket, object_name, body, content_type):
        resp = self.client.put_object(
            Bucket=bucket,
            Key=object_name,
            Body=body,
            ContentType=content_type,
        )
        return resp.get("ETag")


def get_provider():
    """Factory used by UPLOADKIT_STORAGE_PROVIDER."""
    return Boto3S3Storage(
        access_key=settings.AWS_ACCESS_KEY_ID,
        secret_key=settings.AWS_SECRET_ACCESS_KEY,
        region=getattr(settings, "AWS_S3_REGION_NAME", "us-east-1"),
        endpoint_url=getattr(settings, "AWS_S3_ENDPOINT_URL", None),
    )
```

**AWS S3** (`settings.py`):

```python
AWS_ACCESS_KEY_ID = "AKIA..."
AWS_SECRET_ACCESS_KEY = "..."
AWS_S3_REGION_NAME = "eu-west-1"
# AWS_S3_ENDPOINT_URL unset → real AWS
UPLOADKIT_STORAGE_PROVIDER = "myapp.storage.get_provider"
UPLOADKIT_BUCKET = "my-prod-bucket"
```

**MinIO** (`settings.py`):

```python
AWS_ACCESS_KEY_ID = "minioadmin"
AWS_SECRET_ACCESS_KEY = "minioadmin"
AWS_S3_REGION_NAME = "us-east-1"
AWS_S3_ENDPOINT_URL = "http://127.0.0.1:9000"
UPLOADKIT_STORAGE_PROVIDER = "myapp.storage.get_provider"
UPLOADKIT_BUCKET = "uploads"
```

## Quick Start (view)

```python
# myapp/views.py
from django.conf import settings
from django.http import JsonResponse
from uploadkit import Uploader, UploadPolicy, UploaderError
from uploadkit_django import as_uploadable, get_storage_provider, json_error_response
from uploadkit_security import default_validators


def notify(result):
    ...


def upload_view(request):
    storage = get_storage_provider()  # Boto3S3Storage for AWS or MinIO
    policy = UploadPolicy(
        max_size=5 * 1024 * 1024,
        allowed_extensions=frozenset({"png"}),
        allowed_mime_types=frozenset({"image/png"}),
        validators=default_validators(),
    )
    uploaded = request.FILES["file"]
    try:
        result = Uploader(policy, storage).upload(
            as_uploadable(uploaded),
            bucket=settings.UPLOADKIT_BUCKET,
            object_name=uploaded.name,
            after_upload=notify,  # or a Celery-like task with .delay
        )
    except UploaderError as exc:
        return json_error_response(exc)
    return JsonResponse({
        "object_name": result.object_name,
        "sha256": result.sha256,
        "etag": result.etag,
    })
```

## After-upload

Pass Core `after_upload` on `Uploader.upload`: a sync callback `(result) -> None`, or a Celery-like object with `.delay(**result.as_task_kwargs())`. The hook runs once after a successful put; exceptions propagate. Full semantics: [uploadkit Core README](https://github.com/uploadkit/uploadkit#after-upload-hooks).

## Architecture

Thin adapters over UploadKit Core. Django's `UploadedFile` duck-types `UploadableFile`; `as_uploadable` makes that explicit.

## Public API

| Symbol | Kind |
|--------|------|
| `as_uploadable` | Public |
| `json_error_response` / `status_for_error` / `error_payload` | Public |
| `get_storage_provider` | Public |

## Changelog

See [CHANGELOG.md](CHANGELOG.md).

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).
