django-capo-s3¶
A Django file storage backend for S3-compatible object stores, built on the
capo-s3 client instead of boto3 — a drop-in alternative to
django-storages[s3].
Features¶
- No boto3 — built on the lightweight, fully-typed capo-s3 client. The package itself is fully typed and strict-mypy clean.
- Media and static storages —
S3Storagefor media, plusS3StaticStorage(plain) andS3ManifestStaticStorage(content-hashed names +staticfiles.jsonmanifest for cache-busting). See Static files. - Download URLs — presigned URLs (with per-call
expireand response overrides), unsigned public URLs, and CloudFront-signed URLs. See Download URLs. - Fast
collectstatic— the hashing pass lists the bucket once and skips re-uploading unchanged assets, so an unchanged deploy costs no uploads. - Streaming uploads — single-
PUTuploads that switch to a concurrent multipart transfer above a configurable threshold; transparent gzip for eligible content types. - Region and bucket routing —
storage.for_region("eu-central-1")andstorage.for_bucket("archive")clone the storage at runtime, cached per value so each reuses one client and connection pool. - Any S3-compatible provider — AWS, MinIO, DigitalOcean Spaces, Backblaze B2, Cloudflare R2, Yandex, Hetzner, Alibaba OSS. See Different providers.
- Refreshable credentials — AWS profiles, SSO, and IRSA / web-identity tokens are resolved per request and refreshed on expiry (no more silent failures on long-running pods).
- Test helpers — an in-memory S3 service that speaks the real wire protocol, so tests exercise the actual backend with no bucket and no network, and assert on the requests it made. See Testing.
Coming from django-storages? See the comparison.
Installation¶
Python 3.11+ and Django 5.2+ are required.
Quick start¶
Register the backend in Django's STORAGES setting; everything under OPTIONS is passed to the backend:
STORAGES = {
"default": {
"BACKEND": "django_capo_s3.S3Storage",
"OPTIONS": {
"bucket": "my-bucket",
"region": "eu-central-1",
"location": "media",
},
},
"staticfiles": {
"BACKEND": "django_capo_s3.S3ManifestStaticStorage",
"OPTIONS": {"bucket": "my-bucket", "location": "static"},
},
}
With STORAGES["default"] configured, FileField / ImageField just work — uploads, .url, .size, and
.open() all go through the backend:
from django.db import models
class Report(models.Model):
csv = models.FileField(upload_to="reports/")
report = Report.objects.create(csv=uploaded_file)
report.csv.url # presigned URL to the object
report.csv.size # size in bytes
report.csv.open().read() # file contents
Or use the Storage API directly:
from django.core.files.base import ContentFile
from django.core.files.storage import storages
storage = storages["default"]
name = storage.save("reports/june.csv", ContentFile(b"col1,col2\n"))
storage.exists(name) # True
with storage.open(name) as f:
data = f.read()
storage.delete(name) # no error if it's already gone
Credentials are read from the storage OPTIONS, a named AWS profile (session_profile), or the standard AWS
credential chain (environment variables, instance/ECS roles, IRSA) — whichever is configured.