Caching
Caching is one of the easiest ways to make a Django app feel faster.
In this note, you will learn:
- what caching actually is,
- when you should use it (and when you shouldn’t),
- the cache backends (storage options) that Django supports,
- how to set up Redis as a cache backend,
- and two common ways to add caching inside your views.
We use Redis in our examples, but the basic ideas work the same way with other backends too.
What is Caching?
Section titled “What is Caching?”Caching means saving the result of some expensive work (like database queries, calls to outside APIs, or heavy calculations) into a temporary storage spot called a cache.
The next time the same request comes in, Django can just hand back the saved (cached) result instead of doing all that work all over again.
In Django, you can add caching at a few different levels:
- view-level caching,
- template fragment caching,
- low-level caching (where you manually call
cache.get()/cache.set()).
Use Caching or Not?
Section titled “Use Caching or Not?”Caching works best when something is expensive to calculate, but doesn’t change very often.
Example: A report page that runs heavy database queries, but the numbers only get updated once a day.
Caching might not be the right fit for data that changes all the time or needs to be real-time, because users could end up seeing old (stale) information.
When deciding on a caching plan, think about:
- how fresh the data needs to be,
- how often people request it,
- how expensive it is to calculate,
- how much “staleness” (outdated info) you can live with.
Keep in mind that caching uses up RAM and CPU resources. It can also make your app a bit more complex, so it’s worth testing and keeping an eye on your setup.
Always check:
- your cache hit rate (how often the cache is actually used instead of recalculating),
- how much faster your responses get,
- any side effects from showing old (stale) data,
- how and when the cache gets cleared or refreshed (invalidation).
Cache Backends
Section titled “Cache Backends”Django supports several different cache backends (places to store cached data), including:
- In-Memory Cache: This is the default option, and it stores data right in memory (RAM). It’s fast, but it’s not a good fit for production, because all the cached data disappears whenever the server restarts.
- File-Based Cache: This backend saves cached data into files on your disk. It’s slower than in-memory caching, but it can work fine for development or small apps.
- Database Cache: This backend stores cached data inside a database table. It sticks around longer than in-memory caching, but it adds a bit of extra load to your database.
- Memcached: This is a fast, distributed caching system, built for production use, that can cache data across many servers at once.
- Redis: Just like Memcached, Redis is an in-memory data store that you can also use for caching. On top of that, it adds extra features like saving data to disk (persistence) and support for more complex data types.
Demo Slow API Endpoint
Section titled “Demo Slow API Endpoint”Let’s first build a deliberately slow endpoint, so we can clearly see the difference once caching is added.
from rest_framework.decorators import api_viewfrom rest_framework.response import Responseimport requests
@api_view(['GET'])def slow_api(request): data = requests.get("https://httpbin.org/delay/3").json() # Simulate a slow API call with a 3-second delay return Response(data)Redis is an in-memory data store, and it’s one of the most common cache backends used in production for Django apps.
To use Redis for caching in Django, install django-redis and set up CACHES inside your settings.
Installing and Configuring Redis and Caching
Section titled “Installing and Configuring Redis and Caching”-
Add Redis service(s) to your
docker-compose.yml:services:redis:image: rediscontainer_name: redisrestart: alwaysports:- 6379:6379volumes:- ./data/redis:/datacommand: ["redis-server", "--appendonly", "yes"] # Enable data persistence (optional)redisinsight:image: redislabs/redisinsightcontainer_name: redisinsightports:- 8001:8001volumes:- ./data/redisinsight:/db -
Start the Redis service using Docker Compose:
Terminal window docker compose up -d redis redisinsightYou can also use
docker-composeinstead, if that’s what your system supports. -
Install the
django-redispackage:Docs: https://github.com/jazzband/django-redis
Terminal window uv add django-redis -
Set up your Django settings to use Redis as the cache backend:
# settings.pyimport osCACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache","LOCATION": os.getenv("REDIS_URL", "redis://localhost:6379/3"),"TIMEOUT": 60 * 15, # Cache timeout in seconds (15 minutes) (optional)"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",}}} -
Set up caching inside your view:
There are two common ways to do this:
- view-level caching with
cache_page(simpler, and the recommended option), - low-level caching, if you want more custom control.
-
Function-based views:
from django.views.decorators.cache import cache_pagefrom rest_framework.decorators import api_viewfrom rest_framework.response import Responseimport requests@api_view(['GET'])@cache_page(60 * 15) # Cache for 15 minutesdef slow_api(request):data = requests.get("https://httpbin.org/delay/3").json()return Response(data) -
Class-based views:
from django.views.decorators.cache import cache_pagefrom django.utils.decorators import method_decoratorfrom rest_framework.response import Responsefrom rest_framework.views import APIViewimport requestsclass SlowAPIView(APIView):@method_decorator(cache_page(60 * 15)) # Cache for 15 minutesdef get(self, request):data = requests.get("https://httpbin.org/delay/3").json()return Response(data)
from django.core.cache import cachefrom rest_framework.decorators import api_viewfrom rest_framework.response import Responseimport requests@api_view(['GET'])def slow_api(request):key = 'slow_data'data = cache.get(key)if data is None:data = requests.get("https://httpbin.org/delay/3").json()cache.set(key, data, 60 * 15) # Cache for 15 minutesreturn Response(data)For endpoints that show user-specific data (like dashboards or profile pages), be careful with simple shared caching, unless your cache key also includes something that identifies the user.
- view-level caching with
Where Can We Use Caching?
Section titled “Where Can We Use Caching?”It’s a good idea to use caching anywhere you have repeated requests for data that isn’t changing every second:
- Public API endpoints that send back the same response to lots of different users.
- Heavy list pages with filtering or sorting that keep hitting the database again and again.
- Dashboard cards or stats that get recalculated every single time the page refreshes.
- Calls to outside APIs that are slow or have rate limits (a cap on how many times you can call them).
- Template fragments like sidebars, category lists, and “popular posts” widgets.
- Expensive results that take real effort to compute, like recommendation lists or report summaries.
Be careful with caching, or avoid it altogether, for:
- Real-time data (like a stock ticker, a live match score, or live chat).
- Highly personal or permission-sensitive responses, unless your cache keys are properly scoped to each user.
- Important flows where old (stale) data could lead to the wrong decision being made (for example, checking someone’s payment balance).