Skip to content

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.

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()).

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).

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.

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_view
from rest_framework.response import Response
import 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”
  1. Add Redis service(s) to your docker-compose.yml:

    services:
    redis:
    image: redis
    container_name: redis
    restart: always
    ports:
    - 6379:6379
    volumes:
    - ./data/redis:/data
    command: ["redis-server", "--appendonly", "yes"] # Enable data persistence (optional)
    redisinsight:
    image: redislabs/redisinsight
    container_name: redisinsight
    ports:
    - 8001:8001
    volumes:
    - ./data/redisinsight:/db
  2. Start the Redis service using Docker Compose:

    Terminal window
    docker compose up -d redis redisinsight

    You can also use docker-compose instead, if that’s what your system supports.

  3. Install the django-redis package:

    Docs: https://github.com/jazzband/django-redis

    Terminal window
    uv add django-redis
  4. Set up your Django settings to use Redis as the cache backend:

    # settings.py
    import os
    CACHES = {
    "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",
    }
    }
    }
  5. 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_page
      from rest_framework.decorators import api_view
      from rest_framework.response import Response
      import requests
      @api_view(['GET'])
      @cache_page(60 * 15) # Cache for 15 minutes
      def 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_page
      from django.utils.decorators import method_decorator
      from rest_framework.response import Response
      from rest_framework.views import APIView
      import requests
      class SlowAPIView(APIView):
      @method_decorator(cache_page(60 * 15)) # Cache for 15 minutes
      def get(self, request):
      data = requests.get("https://httpbin.org/delay/3").json()
      return 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.

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).