skip to content

In Django, how do you configure more than one cache in the CACHES setting and use a non-default cache from your code?

level: juniorimportance: should knowfreq 45%

answer

  1. a dictionary of named entries
  2. one alias is mandatory
  3. BACKEND and LOCATION per entry
  4. caches[...] versus the cache proxy

basics

~10 s

Django's CACHES setting maps alias names to a dict with BACKEND, LOCATION and options; a 'default' alias is required. Code uses django.core.cache.cache for the default and caches['alias'] for any other.

solid answer

~30 s

`CACHES` is a dict of dicts: each key is an **alias**, each value names a `BACKEND` class path plus optional `LOCATION`, `TIMEOUT`, `OPTIONS`, `KEY_PREFIX`, `VERSION` and `KEY_FUNCTION`. A `"default"` alias must exist, otherwise the system check `caches.E001` reports an error. In code, `from django.core.cache import cache` gives the default alias, and `from django.core.cache import caches` then `caches["reports"]` gives any other. `caches[alias]` returns the same object for repeated lookups in one thread and a separate instance per thread; an alias that is not configured raises `InvalidCacheBackendError`. If you never define `CACHES`, Django still has a cache: the default is `LocMemCache`.

code

python · 19 lines
python
# settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "TIMEOUT": 600,
    },
    "reports": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "report_cache",
        "TIMEOUT": 3600,
    },
}

# anywhere in the project
from django.core.cache import cache, caches

cache.set("homepage:banner", "Rain later today")
caches["reports"].set("monthly:2026-08", {"rows": 412})

go deeper

for a junior

Recall the shape: CACHES is a dict of aliases, 'default' is mandatory, cache is the default alias and caches['name'] reaches the others.

for a middle

Explain the per-alias keys and their defaults, especially TIMEOUT 300 and LocMemCache as the implicit default backend.

for a senior

Show why you would split aliases by lifetime, store or purge scope, and how session and middleware settings select an alias by name.

for a principal

Frame alias design as blast-radius control: which data can be flushed together, and which data every process must share.

## What the CACHES setting is Django's cache framework (`django.core.cache`) is configured by one setting, **`CACHES`**. It is a nested dictionary: every top-level key is an **alias** (a name your code uses), and every value is a dictionary describing one cache connection. You can declare as many aliases as you like, but one of them must be called **`default`**. If a project never sets `CACHES`, Django falls back to its built-in default from `global_settings.py`: ```python CACHES = { "default": { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", } } ``` So "we have no cache configured" is never quite true: there is always a local-memory cache behind `default`. ## The keys each alias accepts | Key | Default | What it means | |---|---|---| | `BACKEND` | none (required) | Dotted path to the backend class, e.g. `django.core.cache.backends.redis.RedisCache` | | `LOCATION` | `""` | Where the data lives: a Redis URL, `host:port` for Memcached, a table name, a directory, or a name for a local-memory store | | `TIMEOUT` | `300` | Default lifetime in seconds; `None` means never expire, `0` means expire immediately | | `OPTIONS` | `None` | Backend-specific extras; the Redis and Memcached backends pass them to the client library | | `KEY_PREFIX` | `""` | String mixed into every key this alias writes | | `VERSION` | `1` | Default version number mixed into every key | | `KEY_FUNCTION` | the built-in joiner | Dotted path to a function that builds the final key | Only `BACKEND` is truly needed; everything else has a default. ## Reaching an alias from code There are two public entry points in `django.core.cache`: - **`cache`** is a proxy for `caches["default"]`. It is what most application code imports. - **`caches`** is a dict-like handler. `caches["reports"]` returns the backend instance for the `reports` alias, creating it on first use. Two details interviewers like: 1. Within one thread, `caches["reports"] is caches["reports"]` is `True`; each thread gets its own backend instance, which keeps client objects thread-safe. 2. Asking for an alias that is not in `CACHES` raises **`InvalidCacheBackendError`** rather than silently handing you a dummy cache. Other parts of Django accept an alias name instead of a backend object: the `cache_page` decorator takes `cache=`, the cache middleware reads `CACHE_MIDDLEWARE_ALIAS`, and a cache-backed session store reads `SESSION_CACHE_ALIAS`. Each of those defaults to `"default"`. ## How Django builds a backend for an alias The `caches` handler creates backends lazily. The first time an alias is requested, Django copies that alias's dictionary, removes `BACKEND` and `LOCATION` from it, imports the class named by `BACKEND` and calls it as `BackendClass(location, params)`, where `params` is whatever is left (`TIMEOUT`, `OPTIONS`, `KEY_PREFIX` and so on). Consequences worth knowing: - A misspelt `BACKEND` path does not fail at import of `settings.py`; it fails on first use of that alias with `InvalidCacheBackendError` ("Could not find backend ..."). - Client libraries such as `redis-py` or `pymemcache` are imported only when a backend that needs them is built, so a project that never touches a Redis alias does not need the package installed. - Django connects a receiver to the `request_finished` signal that calls `close()` on every cache backend created so far, so backends that hold connections can release them at the end of each request; for most backends this is a no-op. ## Why teams use several aliases - **Different lifetimes**: a long-lived alias for rarely changing lookups next to a short-lived default. - **Different stores**: a shared Redis cache for data every worker must see, and a small `LocMemCache` alias for per-process memoisation of something cheap and immutable. - **Isolation**: a separate `KEY_PREFIX` or `LOCATION` so a bulk purge of one alias does not touch another's data. - **Tests**: overriding `CACHES` to `DummyCache` or `LocMemCache` for a test run without touching application code. ## Common mistakes - Removing the `default` alias while adding named ones: `manage.py check` then reports `caches.E001`, "You must define a 'default' cache in your CACHES setting." - Assuming a second `LocMemCache` alias is a separate store when both share the same `LOCATION`; local-memory stores are keyed by `LOCATION`, so give each one a distinct name. - Importing `cache` in code that should target another alias, so writes land in `default` and reads from `caches["reports"]` miss.

  • What happens at startup if CACHES defines only a 'redis' alias and no 'default'?
    The system check framework reports `caches.E001`: "You must define a 'default' cache in your CACHES setting." Commands that run checks, such as `runserver` and `migrate`, stop on that error. `django.core.cache.cache` is a proxy for the `default` alias, so it has nothing to point at.
  • What does caches['nightly'] do if 'nightly' is not a configured alias?
    It raises `InvalidCacheBackendError`, a subclass of `ImproperlyConfigured`. Django never substitutes a dummy backend for a missing alias, so a typo in an alias name fails loudly on first use rather than silently caching nothing.

saying these in an interview costs you the question

  • Without a CACHES setting Django has no cache at all.
  • The 'default' alias is optional as long as some alias exists.
  • An unknown alias in caches[...] quietly returns a no-op cache.
  • Each alias needs its own import, like from django.core.cache import reports.
  • Two LocMemCache aliases are always separate stores whatever their LOCATION.