In Django's CACHES setting, how do KEY_PREFIX, VERSION and KEY_FUNCTION combine into the key actually stored in the cache backend?
answer
- your key is not the stored key
- three parts joined
- colon separators by default
- a dotted path replaces the joiner
basics
~20 sDjango never stores your key verbatim: the default key function joins KEY_PREFIX, VERSION and the key with colons, so with KEY_PREFIX 'site' the key 'forecast' is stored as 'site:1:forecast'. KEY_FUNCTION swaps in a custom joiner, for example one that hashes long keys.
solid answer
~40 sEach backend calls `make_key()`, which passes three parts to the alias's key function: `KEY_PREFIX` (default `""`), the version (the alias's `VERSION`, default `1`, unless a call overrides it) and your key. The default `default_key_func` returns `"%s:%s:%s" % (key_prefix, version, key)`, so `cache.set("forecast", …)` with no prefix is stored as `:1:forecast`. `KEY_PREFIX` keeps two projects or environments sharing one server from reading each other's entries; bumping the alias-wide `VERSION` makes every existing key unreachable at once after a change in the cached data's shape. `KEY_FUNCTION` is a dotted path, or a callable, with the same `(key, key_prefix, version)` signature, used for hashing keys that exceed Memcached's 250-character limit or for a different layout.
code
python · 19 lines# myproject/cache_keys.py
import hashlib
def hashed_key(key, key_prefix, version):
digest = hashlib.sha256(str(key).encode()).hexdigest()
return f"{key_prefix}:{version}:{digest}"
# settings.py
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
"KEY_PREFIX": "wx",
"VERSION": 3,
"KEY_FUNCTION": "myproject.cache_keys.hashed_key",
}
}go deeper
Recall that Django adds a prefix and a version to every key, so the stored key differs from the one in your code.
Explain default_key_func's prefix:version:key format, the defaults, and what KEY_PREFIX and alias-wide VERSION are each for.
Use KEY_PREFIX to isolate environments on shared servers, VERSION bumps for shape changes, and a hashing KEY_FUNCTION for Memcached's key limits.
Set a key-naming convention across services sharing cache servers so collisions and mass invalidations are designed, not discovered.
## The key you pass is not the key that is stored When code calls `cache.set("forecast:paris", data)`, Django's backend does not send `forecast:paris` to Redis, Memcached, the database table or the file system. Every backend first calls **`make_key(key, version=None)`**, defined on `BaseCache`, which builds a final key from three parts: 1. **`KEY_PREFIX`**: a string from the alias's `CACHES` entry, default `""`. 2. **The version**: the alias's `VERSION`, default `1`, unless a particular call passes its own `version=`. 3. **Your key**. It then hands those three parts to the alias's **key function**. ## The default key function The default is `default_key_func` in `django/core/cache/backends/base.py`: ```python def default_key_func(key, key_prefix, version): return "%s:%s:%s" % (key_prefix, version, key) ``` So with the defaults, `forecast:paris` is stored as `:1:forecast:paris`, and with `KEY_PREFIX = "wx"` it becomes `wx:1:forecast:paris`. Looking in `redis-cli` for `forecast:paris` and finding nothing is a common first surprise. ## What each setting is for | Setting | Default | Main use | |---|---|---| | `KEY_PREFIX` | `""` | Namespace one project or environment inside a shared cache server | | `VERSION` | `1` | Make every existing entry of an alias unreachable in one deploy | | `KEY_FUNCTION` | `default_key_func` | Change the layout, e.g. hash long keys or add an app label | - **`KEY_PREFIX`** matters whenever a cache server is shared: staging and production, or two services. Without it, both write `:1:homepage` and read each other's values, which becomes a very hard bug when the cached data has different shapes. - **Alias-wide `VERSION`** is a blunt, useful tool. If a release changes the structure of cached objects, bumping `VERSION` from `1` to `2` makes the new code ignore every old entry without flushing the server; the old entries simply age out. - **`KEY_FUNCTION`** accepts a dotted path string (or a callable) to a function with the same `(key, key_prefix, version)` signature. A common body hashes the key so it always fits Memcached's limit. Per-key version arguments and `incr_version()` are part of the low-level cache API, not of this configuration. ## Keys and backend limits - **Memcached** rejects keys longer than 250 characters or containing whitespace or control characters; Django's Memcached backends raise **`InvalidCacheKey`** for them. - The **other built-in backends** only emit **`CacheKeyWarning`** for the same keys, to keep code portable. You can silence that warning, or override `validate_key()` in a backend subclass. - The prefix and version count toward the length, so a long `KEY_PREFIX` makes the limit bite sooner. ## Related prefixes that are easy to confuse - **`CACHE_MIDDLEWARE_KEY_PREFIX`** (default `""`) is an extra prefix used only by the whole-page cache middleware. It is combined with the alias's `KEY_PREFIX`; it does not replace it. - A `KEY_PREFIX` narrows what `get` and `set` see, but it does **not** scope bulk operations on the server: see the separate question about `cache.clear()`. ## Choosing a prefix and version scheme A few conventions keep keys predictable across a team: - **Prefix by deployment, not by developer whim**: something like the project name plus the environment, for example `forecast-prod` and `forecast-staging`, so two environments sharing a server never collide. - **Keep the application part of the key structured**: `forecast:paris:2026-09-26` is easier to reason about, and to delete deliberately, than an opaque string built in several places. - **Treat `VERSION` as a release lever**: record in the release notes when it was bumped, because a bump means a cold cache and a temporary rise in database load while entries are rebuilt. - **Hash only when you need to**: a hashing `KEY_FUNCTION` makes keys unreadable in the cache server's own tools, so reserve it for backends with strict key limits or keys built from long user input. ## A worked example With `KEY_PREFIX = "wx"`, `VERSION = 3` and a hashing key function: - `cache.get("forecast:paris")` asks the key function for a key built from `("forecast:paris", "wx", 3)`. - The function returns, for example, `wx:3:` followed by a SHA-256 hex digest. - Every web process computes the same final key, so they all hit the same entry.
- A release changes the shape of every cached object. How can CACHES settings retire the old entries without flushing the server?Bump the alias's `VERSION`, say from `1` to `2`. The default key function puts the version into every stored key, so new code builds different keys and never reads the old entries, which expire on their own timeouts. Nothing is deleted, so other data on the server is untouched.
- How does CACHE_MIDDLEWARE_KEY_PREFIX relate to the alias's KEY_PREFIX?They stack. `CACHE_MIDDLEWARE_KEY_PREFIX` is an additional prefix the whole-page caching middleware puts into the keys it builds, and those keys then pass through the alias's key function, which adds `KEY_PREFIX`. Setting one does not replace the other.
saying these in an interview costs you the question
- Django stores the key exactly as the code passes it.
- KEY_PREFIX replaces the version part of the stored key.
- KEY_FUNCTION receives only the key, not the prefix and version.
- Bumping VERSION deletes old entries from the cache server.
- CACHE_MIDDLEWARE_KEY_PREFIX overrides the alias's KEY_PREFIX.