Skip to main content

django-zerobucket

A Django Storage backend adapter for ZeroBucket -- database-native image storage, plugged into Django's existing FileField/ImageField machinery. No new field type to learn, works with existing forms/admin/migrations the same way django-storages' S3/GCS backends do for their targets.

Installation

pip install django-zerobucket

Requires Django >= 5.2 and a PostgreSQL database ZeroBucket can store images in (see the core zerobucket package for schema/setup details -- this package doesn't change any of that, it's a thin adapter on top).

Setup

# settings.py
INSTALLED_APPS = [
    ...,
    "django_zerobucket",   # needed for the zerobucket_* management commands
]

STORAGES = {
    "default": {
        "BACKEND": "django_zerobucket.storage.ZeroBucketStorage",
        "OPTIONS": {"database_url": "postgresql://user:pass@localhost/mydb"},
    },
}
# or, instead of OPTIONS above:
ZEROBUCKET_DATABASE_URL = "postgresql://user:pass@localhost/mydb"
# project urls.py -- required for ZeroBucketStorage.url() to resolve
from django.urls import include, path

urlpatterns = [
    ...,
    path("images/", include("django_zerobucket.urls")),
]
# models.py -- just a normal ImageField/FileField
class Product(models.Model):
    photo = models.ImageField(upload_to="ignored")

That's it. product.photo.save(...), product.photo.read(), product.photo.url, product.photo.delete() -- all work exactly like any other Django Storage backend from here.

The one thing worth knowing before you adopt this

The name Django stores for each file is a ZeroBucket image id (a UUID), not a filesystem-style path. Every other Django Storage backend builds a path out of upload_to= plus the uploaded filename (e.g. "uploads/2024/photo.jpg") -- ZeroBucket is id-addressed storage, not path-addressed, so there's no "directory" for a file to live in. upload_to= is still required by Django's field API but is effectively ignored by this backend.

This is invisible for the vast majority of normal usage (instance.photo.url, .read(), .open(), template {{ instance.photo.url }} all work exactly the same) -- it only matters if your code inspects the raw instance.photo.name string and expects something path-shaped. The original upload filename isn't lost, though: it's preserved in ZeroBucket's own metadata and retrievable via the core zerobucket client's metadata()/get() methods, even though Django's own name field won't show it.

Serving images

ZeroBucketStorage.url() reverses to a built-in view (ServeImageView) that streams the image back via ZeroBucket's own get_stream() -- a real use of the core library's streaming feature, not just a demonstration of it.

No built-in access control. This is a deliberate first-pass scope decision: ZeroBucket's own before_get/before_put hooks exist for exactly this, but wiring a context= through Django's request/auth system into those hooks is a real design question of its own (whose context -- the request? the user? something else?) that's out of scope for this first pass. If you need access control on served images, protect the view the normal Django way:

from django.contrib.auth.decorators import login_required
from django_zerobucket.views import ServeImageView

urlpatterns = [
    path("images/<uuid:image_id>/",
         login_required(ServeImageView.as_view()),
         name="my_protected_image"),
]

(and don't also include("django_zerobucket.urls"), or the same images stay reachable unprotected there too.)

Want images served some other way entirely -- a CDN in front of tiered S3 objects, presigned S3 URLs for images you've moved via tier_to_object_storage(), whatever -- subclass ZeroBucketStorage and override url(). This backend has no special knowledge of tiering one way or the other, since transparent reads mean it doesn't need any.

Management commands

Thin wrappers around the core zerobucket CLI's already-tested cmd_info/cmd_verify/cmd_tier -- not reimplementations. Same behavior, same flags, just reading ZEROBUCKET_DATABASE_URL from Django settings instead of requiring --database-url:

./manage.py zerobucket_info
./manage.py zerobucket_verify
./manage.py zerobucket_verify --sample 100
./manage.py zerobucket_tier IMAGE_ID --bucket my-bucket
./manage.py zerobucket_tier --all --bucket my-bucket
./manage.py zerobucket_tier --min-size 2000000 --older-than 90 --bucket my-bucket

zerobucket_tier needs boto3 (pip install "zerobucket[s3]") the same way the standalone zerobucket tier CLI command does -- not pulled in by installing django-zerobucket itself. zerobucket_verify/ zerobucket_tier exit non-zero on failure (checksum mismatch, tier failure), same convention the standalone CLI uses, so both are usable in a cron job or CI/deploy step, not just interactively.

What this package deliberately doesn't do (yet)

  • No custom model field. A Storage adapter was judged the higher- leverage, more idiomatic choice for a first pass -- it composes with Django's existing FileField/ImageField, forms, and admin, rather than asking every adopting project to learn a new field type.
  • No async support. The core zerobucket package ships AsyncZeroBucket; this package's ZeroBucketStorage/ServeImageView are both built on the sync client. Django's own async views/ORM paths aren't wired up to it yet.
  • No signal-based cleanup. Django does not automatically delete storage files when a model instance is deleted (this is a general Django behavior, not specific to this backend -- confirmed directly, not assumed) -- if you want images cleaned up when their owning model is deleted, add a post_delete signal calling instance.photo.delete() yourself, same as you would with any other Django Storage backend.
  • No dedup=True/tiering-aware URLs. Both are fully supported at the core zerobucket client level (pass the relevant options through STORAGES' OPTIONS) and work transparently through this adapter -- there's just no Django-specific UI/behavior built on top of them yet (e.g. an admin action to bulk-tier selected objects).

Development

cd packages/django-zerobucket
pip install -e ".[dev]"
export ZEROBUCKET_TEST_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/zerobucket_test
pytest -q
ruff check src tests

Tests run against a real PostgreSQL instance, used for two separate roles simultaneously: Django's own DATABASES (its internal tables plus this test suite's dummy Product model) and ZEROBUCKET_DATABASE_URL (where ZeroBucket actually stores image bytes) -- see tests/django_settings.py's module docstring for why those are kept conceptually distinct even when pointed at the same physical server in this test setup.

License

MIT, same as the core zerobucket package.

Release files for django-zerobucket 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-zerobucket 0.1.0
File Size Uploaded
django_zerobucket-0.1.0.tar.gz 16.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-zerobucket 0.1.0
File Interpreter ABI Platform
django_zerobucket-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.4 kB

Release files / django_zerobucket-0.1.0.tar.gz

Download URL django_zerobucket-0.1.0.tar.gz
Size 16.5 kB
Tags Source
SHA-256 checksum
How to use checksums
88129608e5827960dd20f95e1c22f7fcbcddf9ffd85aee201e8d42e1290214ae
BLAKE2b-256 checksum
How to use checksums
5f065cb256b1dac1c2560acbff72d7a98a596c0819239e1b9cb1cf7b4ebc60a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release files / django_zerobucket-0.1.0-py3-none-any.whl

Download URL django_zerobucket-0.1.0-py3-none-any.whl
Size 14.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
948b33846f16e15da9e7bfb956abf721fa3e9b89d6525d97754c0f82d85f0f03
BLAKE2b-256 checksum
How to use checksums
dfd178653b6dd1641ee87b101ae8e44006564b03d0fe8c301b3b4c31e4a83532
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.9

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page