Cache in Django web application

Why to use cache in a web application?

We know that the dynamic data received on web application is an extension to the static websites, which used to serve only static file over HTTP protocol. When we send dynamic web page each time a user requests, the server does lot of computation in generation of dynamic data: from calling database servers to template rendering to business logic. These computation are lot expensive than just service static files. It is ok for a small or a low traffic website. Consider this for a very famous website with huge traffic.

What can we do in such cases? Cache can be used to save lot of repeated processing. To cache something is to save the result of an expensive computation so that you don’t have to do it next time. A pseudocode for caching a dynamically generated web page is:

given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page

Cache with Django web application

Django comes with a robust cache system that lets you save dynamic pages so they don’t have to be computed for each request. For convenience, Django offers cache with different granularity — from entire website to pages to part of pages to DB query results to any objects in memory.

Browser caching: Django also supports browser-based caches, where you have to provide hints (via HTTP headers) about which parts of your site should be cached, and how.

Setting up the cache

The cache system requires a small amount of setup. Especially the storage of cache data — whether in a database, on the filesystem or in memory. This is an important decision that affects your cache’s performance.

Memcached based cache:

The fastest, most efficient type of cache supported natively by Django. Memcached is an entirely memory-based cache server, originally developed to handle high loads at LiveJournal.com and subsequently open-sourced by Danga Interactive. It is used by sites such as Facebook and Wikipedia to reduce database access and dramatically increase site performance.

To use Memcached running on localhost (127.0.0.1) port 11211, use the below configuration in settings.py

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}

Memcached allows cache system to be running over multiple servers. To use Memcached instances running on IP address 172.19.26.240 and 172.19.26.242, both on port 11211 add the below configuration:

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}

Other caching backends:

  • Database caching
  • Filesystem caching
  • Local-memory caching
  • Dummy caching (for development purpose only)

Cache usage

Level 1. Per-site cache:

Once the cache is set up, the simplest way to use caching is to cache your entire site. You’ll need to add middleware in settings.py:

MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]

Level 2: Per-view cache

A more granular way to use the caching is by caching the output of individual views. Just addcache_page decorator as below to cache view’s response:

from django.views.decorators.cache import cache_page@cache_page(60 * 15)
def my_view(request):
...

In the above example, the result of the my_view() view will be cached for 15 minutes. Both per-view cache and per-site cache uses URL as key. If multiple URLs point at the same view, each URL will be cached separately. In my_view example, if the URL configuration is as below, then requests to /foo/1/ and /foo/23/ will be cached separately, as you may expect.

urlpatterns = [
path('foo/<int:code>/', my_view),
]

Level 3: Low-level cache API

Sometimes, caching an entire rendered page could be an overkill. For example, your site includes a view whose results depend on several expensive queries, the results of which change at different intervals. For cases like this, Django exposes a low-level cache API. You can use this API to store objects in the cache with any level of granularity you like. You can cache any Python object that can be pickled safely: strings, dictionaries, lists of model objects etc.

Cache APIs usage:

Reference