action0-django-acache

Django’s Redis cache backend with async methods that are actually async: they talk to the server through redis.asyncio instead of running the sync methods in a worker thread.

pip install action0-django-acache    # or: uv add action0-django-acache

Swap the backend in your settings:

CACHES = {
    "default": {
        "BACKEND": "action0.django_acache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
    },
}

And await the cache in async code as usual:

from django.core.cache import cache


async def dashboard(request):
    stats = await cache.aget("stats")
    ...

Highlights:

  • A drop-in replacement for Django’s RedisCache: same settings, same sync methods (they are Django’s), same keys and serialized values, so sync and async code share one cache.

  • Every async method Django’s backend hands to a thread — aget, aset, aadd, atouch, adelete, ahas_key, aget_many, aset_many, adelete_many, aincr, aclear — is a native coroutine. aincr stays a single, atomic INCR on the server.

  • Connection pools shared per event loop: under ASGI, Django creates a cache instance per request, but all of them reuse the same connections. Pools close themselves when their loop shuts down.

  • ASYNC_OPTIONS for the few redis-py options that differ between the sync and the async client (pool, parser and connection classes, retry objects).

  • Tested against Redis and Valkey. Django 5.2+, redis-py 5.0.1+.

  • Fully typed (checked with mypy strict, pyright and ty), Python 3.11+.

The action0 namespace is simply the one the author likes to use for personal projects.