docs: rewrite Caching category intro

The intro's lead and guide recommended dogpile.cache, which the list no longer carries, and never covered django-redis, which replaced it.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-10-16 18:24:44 +08:00
co-authored by Claude
parent 62ba5e25fe
commit f1e3eff74f
+11 -14
View File
@@ -1,23 +1,20 @@
In memory, on disk, or on a cache server: the Python caching library you want is cachetools, DiskCache, or dogpile.cache. For HTTP responses, it's Hishel.
Expensive function calls get cached in memory by one cachetools decorator. On disk, with no server, no Python caching library asks less of you than DiskCache.
How to choose:
- Function results in one process's memory, with a size limit or a time-to-live: cachetools
- A cache on local disk that processes on one machine share, with no server to run: DiskCache
- Memoizing a function in one process, with a size limit or an expiry time: cachetools
- A persistent cache on local disk that several processes share: DiskCache
- A Django cache backend on local disk: DiskCache
- One cache on memcached or Redis for many processes or servers: dogpile.cache
- HTTP responses in HTTPX or Requests: Hishel
- `Cache-Control` headers and response caching in FastAPI or another ASGI app: Hishel
- Django querysets, invalidated when a model changes: Cacheops
- A Django cache and session store on Redis: django-redis
- Caching HTTP responses in HTTPX or Requests, or setting cache headers in FastAPI: Hishel
- Django ORM queries cached in Redis and invalidated when the data changes: Cacheops
cachetools offers [variants of the standard library's `@lru_cache`](https://cachetools.readthedocs.io/en/stable/) with more cache algorithms, including a `TTLCache` whose items [expire after a time-to-live](https://cachetools.readthedocs.io/en/stable/#cachetools.TTLCache). Wrap a function in [`@cached`](https://cachetools.readthedocs.io/en/stable/#cachetools.cached) and pass it the cache to use. The cache classes [aren't thread-safe](https://cachetools.readthedocs.io/en/stable/#cache-implementations), so when threads share a cache, give `@cached` a `threading.Lock`. The cache lives in your process's memory, so each worker process of a web app keeps its own copy.
cachetools offers [variants of the standard library's `@lru_cache` decorator](https://cachetools.readthedocs.io/en/latest/). Its cache classes have a maximum size and drop items by different rules, like least recently used or a per-item time-to-live. Wrap a function in `@cached` with one of them: the README's `@cached(cache=TTLCache(maxsize=1024, ttl=600))` [keeps results for ten minutes](https://github.com/tkem/cachetools). To keep the `lru_cache` API instead, use the [`cachetools.func` decorators](https://cachetools.readthedocs.io/en/latest/#module-cachetools.func).
DiskCache is a [disk and file backed cache library](https://grantjenks.com/docs/diskcache/) in pure Python, built on SQLite, with no other process to run. Create a `Cache` with a directory path: [two `Cache` objects on the same directory](https://grantjenks.com/docs/diskcache/tutorial.html#cache) can live in separate processes, so the workers on one machine share one cache. Wrap a function in [`@cache.memoize()`](https://grantjenks.com/docs/diskcache/tutorial.html#fanoutcache), which takes arguments like `lru_cache`'s. In Django, set [`diskcache.DjangoCache`](https://grantjenks.com/docs/diskcache/tutorial.html#djangocache) as the cache `BACKEND`. Keep the directory on a local disk, since SQLite [isn't recommended on NFS mounts](https://grantjenks.com/docs/diskcache/tutorial.html#caveats).
DiskCache keeps keys and values in [a SQLite database and a directory of files](https://grantjenks.com/docs/diskcache/), with no server process to run. Point a `Cache` at a directory, and [other threads and processes that open the same directory](https://grantjenks.com/docs/diskcache/tutorial.html#tutorial-cache) share its entries. Its `memoize()` decorator takes [arguments like `lru_cache`'s](https://grantjenks.com/docs/diskcache/tutorial.html#tutorial-fanoutcache), plus an expiry time. In Django, [set `diskcache.DjangoCache` as the `BACKEND`](https://grantjenks.com/docs/diskcache/tutorial.html#tutorial-djangocache) and a directory as the `LOCATION`.
dogpile.cache is [a caching API over backends of any variety](https://dogpilecache.sqlalchemy.org/en/latest/), memcached and Redis among them. You ask it for a value and hand it a function that [creates the value only when needed](https://dogpilecache.sqlalchemy.org/en/latest/usage.html#overview). When the value expires, one worker regenerates it instead of every worker at once. Create a region with `make_region()` at import time, decorate functions with `@region.cache_on_arguments()`, and [call `configure()` later](https://dogpilecache.sqlalchemy.org/en/latest/usage.html#rudimentary-usage) with the backend and expiration time from your config file. When several processes share one Redis or memcached server, turn on the backend's [`distributed_lock`](https://dogpilecache.sqlalchemy.org/en/latest/api.html#dogpile.cache.backends.redis.RedisBackend) so that lock covers them all.
django-redis is a [full-featured Redis cache and session backend](https://github.com/jazzband/django-redis#introduction) for Django. [Set `django_redis.cache.RedisCache` as the `BACKEND`](https://github.com/jazzband/django-redis#configure-as-cache-backend) and a redis-py URL like `redis://127.0.0.1:6379/1` as the `LOCATION`. Sessions then need no extra backend: [point `SESSION_ENGINE` at Django's cache sessions](https://github.com/jazzband/django-redis#configure-as-session-backend), and they live in Redis too. Its clients, parsers, and serializers [are pluggable](https://github.com/jazzband/django-redis#why-use-django-redis).
Hishel caches HTTP responses by [RFC 9111](https://hishel.com/overview.html), the HTTP caching rules browsers follow, in both sync and async code. With HTTPX, [swap your client for Hishel's cache-enabled one](https://hishel.com/httpx.html#quick-start), or [give a client you already have its cache transport](https://hishel.com/httpx.html#cache-transports). With Requests, [mount its cache adapter](https://hishel.com/requests.html#quick-start) on a `Session`. In FastAPI, [a dependency sets `Cache-Control` headers](https://hishel.com/fastapi.html#quick-start) for browsers and CDNs, and its ASGI middleware also caches responses on your server.
Hishel caches HTTP responses [by RFC 9111](https://hishel.com/overview.html), the rules browsers follow. It works in sync and async code, and comes with a backend that stores responses in SQLite. In HTTPX, [use `SyncCacheClient` or `AsyncCacheClient`](https://hishel.com/httpx.html#cache-clients), drop-in replacements for `Client` and `AsyncClient`. In Requests, [mount its `CacheAdapter`](https://hishel.com/requests.html#quick-start) on a `Session`. In FastAPI, [the `cache()` dependency](https://hishel.com/fastapi.html) sends Cache-Control headers to browsers and CDNs, and Hishel's ASGI middleware adds a cache on your server.
Cacheops caches Django querysets in Redis and [invalidates them](https://github.com/Suor/django-cacheops#user-content-invalidation) on `save()`, `delete()`, and many-to-many changes. [Add it to `INSTALLED_APPS`](https://github.com/Suor/django-cacheops#user-content-setup) and point it at its own Redis database, as its docs highly recommend. Turn caching on per app with `'app_name.*'`, since `'*.*'` can also cache tables you don't mean to, like migrations. Call `.cache()` on a queryset to cache it by hand, and wrap a function in [`@cached_as(Article)`](https://github.com/Suor/django-cacheops#user-content-usage) to drop its result whenever an `Article` changes.
Plan how stale entries leave the cache, too. cachetools' decorators add a [`cache_clear()`](https://cachetools.readthedocs.io/en/stable/#memoizing-decorators) function, DiskCache [evicts every key with a given tag](https://grantjenks.com/docs/diskcache/tutorial.html#cache), and a function decorated by dogpile.cache takes [`invalidate()`](https://dogpilecache.sqlalchemy.org/en/latest/api.html#dogpile.cache.region.CacheRegion.cache_on_arguments) with the same arguments you'd call it with.
Cacheops caches Django ORM queries in Redis and [invalidates them on `save()`, `delete()`, and m2m changes](https://github.com/Suor/django-cacheops#invalidation), only where a change could affect the result. Add `cacheops` to `INSTALLED_APPS`, then set `CACHEOPS_REDIS` and [a `CACHEOPS` profile per app](https://github.com/Suor/django-cacheops#setup) like `'app_name.*'`. Profiles with `ops` cache those queries automatically. To cache by hand, [call `.cache()` on a queryset, or decorate a function with `@cached_as`](https://github.com/Suor/django-cacheops#usage), and it still invalidates with the model.