Caching
⚡ Caching
Cache Backends
Like DATABASES, CACHES in settings.py configures where cached data actually lives:
Redis / Memcached
The real production choices — an in-memory store separate from Django itself, fast, and shared across every web server process.
LocMemCache
A simple in-process cache, fine for local development — but each server process gets its own separate cache, which doesn't reflect production behavior.
DummyCache
Implements the same API but never actually stores anything — every get() misses. Useful for tests where you deliberately want caching disabled.
🔑 The Low-Level Cache API
get_or_set() combines the check-then-compute-then-store pattern into one call — the cleanest way to cache an expensive QuerySet result:
Recomputing Every Request vs Caching the Result
Recomputed Every Time
Cached With get_or_set()
Per-View Caching: @cache_page
The coarsest-grained option — caches an entire view's rendered response:
Template Fragment Caching
@cache_page caches the whole response — {% cache %} caches just one part of a template, leaving the rest (a per-user greeting, say) to render fresh every time:
900 is the timeout in seconds; book_list_fragment is an arbitrary name identifying this cached fragment.
Varying the Cache Key
A page cached for one visitor must not be served to another if the content actually differs per-user — Django's vary_on_* decorators fold request details into the cache key itself:
💻 Coding Challenges
Challenge 1: Cache an Expensive Computation
Write a function get_catalog_stats() that aggregates the average price and total count of all books, then use cache.get_or_set() to cache the result for 10 minutes inside a view.
Goal: Practice the low-level cache API's get_or_set() pattern for an expensive query.
Challenge 2: Cache a Template Fragment
Wrap the book list portion of a template in {% cache %} for 5 minutes, while leaving a per-user "Welcome, {{ request.user.username }}" line outside the cached block so it still renders fresh for every request.
Goal: Practice caching only part of a page, not the whole response.
Challenge 3: Invalidate the Cache on Save
Write a post_save signal receiver (per Chapter 4's pattern) on the Book model that calls cache.delete("book_stats") whenever a book is created or updated, so get_catalog_stats()'s cached value from Challenge 1 never goes stale for longer than necessary.
Goal: Practice tying cache invalidation to the actual event that makes cached data stale, instead of just waiting out a timeout.
Caching a book list for 15 minutes means a newly added book — or a price correction — simply won't appear for up to 15 minutes, unless something explicitly clears that cache entry. A timeout alone is a blunt instrument; tying invalidation to the actual event that makes the cache stale (a post_save signal calling cache.delete(), the same signal pattern from Chapter 4) is what keeps a cache both fast and correct. Also worth knowing: if local development uses DummyCache (or never configures a real backend), caching bugs — including a forgotten invalidation — will never surface until the app is running against a real cache backend in production.
🎯 What's Next
With expensive data now cached correctly, the next chapter returns to class-based views from Course 1's introduction and goes much further: Class-Based Views Deep Dive — Django's generic views and mixins for building common patterns with even less code.