Django Caching Levels

·

7 min read

You know a little about Django caching and are able to set up your cache on the back end. Now you need to decide how much data you actually want to cache.

Websites usually come with different files such as templates, forms, sidebars and so much more. Depending on the kind of website you are building, you might want to cache some or all of these files to increase performance and reduce load times for your visitors.

Django allows you, as a developer, to decide which files you want to cache. It does so through 4 main caching levels.

These are what we call Django caching levels.

In this article, we are going to review the 4 levels of caching and explore their capabilities.

Per-site caching

It is considered the simplest form of caching because all you need to do is cache the entire website. You don’t need to break the website down and store certain files. Think of it as cramming everything into a bag and zipping it up.

A beginner who is trying to learn the ropes with caching will benefit from Per-site caching because it is straightforward and will not require you to spend too much time to set up.

This does not mean that you shouldn’t understand the complexities – or lack of- of this caching level.

To set up per-site caching:

  • First, start with the middleware section in your settings.py file.
  • Make sure to set up this section as it appears here in this article.
  • UpdateCacheMiddleware should come first, followed by CommonMiddleware, and lastly, FetchFromCacheMiddleware.
MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
]

Once done, add these 3 settings files right below your Middleware section

CACHE_MIDDLEWARE_ALIAS = 'default'
CACHE_MIDDLEWARE_SECONDS = ‘65432CACHE_MIDDLEWARE_KEY_PREFIX = ‘ ’

What are these 3 things?

  • The _CACHE_MIDDLEWAREALIAS is the alias used for storage. You can set it up as default.
  • The _CACHE_MIDDLEWARESECONDS is the time you want each page of your website to be stored. Remember, the files will be stored temporarily on your visitors' computers. This time is set in seconds.
  • The _CACHE_MIDDLEWARE_KEYPREFIX comes in form of a string and will be automatically attached to all cache keys by the Django server. You could decide to set this as the name of your site in situations where your cache is shared across different sites. Or you could leave it as an empty string.

Per-View Cache

Views are the functions that are mostly responsible for the things we see on the screen. It is possible to set up your cache such that it only stores individual views using Per-view caching.

This uses Django.views.decorated.cache to define a cache_page decorator which then automatically stores a particular view.

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):

The cache_page you have imported takes the cache timeout in seconds as the only argument. So in the code above, the view will be cached for 15 minutes. However, the time had to be multiplied by 60 seconds such that the view will be cached for 900 seconds.

The per-view cache is keyed off in the URL and if you have many URLs pointing at the same view, then each URL will be cached separately. Take for example this url pattern provided by Django.

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

All the different requests directed to this path will be cached directly. And as soon as each url is cached, any subsequent request to the same url will use the same cache.

Per view cache usually works using the default cache. However, it is possible to direct the cache-page decorator to store the views in a specific cache. You can do this by creating an optional keyword argument.

For example, the code below directs the cache-page decorator to store the cached view in a cache called “special-cache”

@cache_page (60 * 15, cache="special_cache")
def my_view(request):

It is possible to override the cache prefix on a per-view basis. The cache_page decorator will take an optional argument, _KeyPrefix which works the same way as the CACHE_MIDDLEWARE_KEY_PREFIX setting does.

So in code, it would look like this:

@cache_page(60 * 15, key_prefix="site1")
def my_view(request):

You can specify the Key-Prefix and cache arguments together in which case the key-prefix argument and the KEY_PREFIX under CACHES will be concancated.

Also, cache_page will automatically set Cache-Control and Expires headers in the response which affects downstream caches.

Specifying Per-View Cache in the URLconf

All of the examples provided above use the cache_page decorator to alter the my_view function. Thus, there is no doubt that the view is cached.

However, this method is not entirely ideal because it ties the decorator _@cachepage to the view. In instances where you need to, let’s say, reuse the view function on another site that does not require caching, it will cause some problems.

To prevent this, specify the per-view cache in the url path instead of the view function. Done correctly, your code should look like this:

from django.views.decorators.cache import cache_page

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

Template Fragment Caching

Think about your template and the amount of data that is presented on it. On a basic site, you could have the headers, the body, a contact section, sidebars, the footer and so much more.

It is possible to cache different sections of your template using the Template Fragment Caching method.

Template fragment caching offers you more control over what you want cached. All you need to do is use the {% load cache %} at the top of your template. Afterward, use the {% cache %} template tag to cache the contents of the block for a specific time.

Say, for example, you wish to cache your sidebar so that it does not need to load every time a visitor revisits your site. You will need at least two arguments i.e. cache timeout in seconds and the name to give the cache fragment.

If you wish to retain the cache fragment forever, set the timeout as None.

The code in your template will look like this:

{% load cache %}
{% cache 500 sidebar %}
    .. Sidebar...
{% endcache %}

What happens then when you want to cache different copies based on some dynamic data?

Say, for example, you want to cache the sidebar for every user of your site. You can use additional arguments. These variables can have filters or not. The arguments will be added to the {% cache %} template tag as a unique identifier for the cache fragment.

An example of your code would look like this:

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. Sidebar for logged-in user...
{% endcache %}

You can learn more about Template Fragment Caching here.

The Low-level Code API

There are times during web development when we don’t want to cache a whole page.

Maybe the page in question has several different components that keep changing with time. Caching such a page will bring a level of inconvenience that could be detrimental to the overall experience of your site’s visitors.

This does not mean that we can’t cache the page. We only need to attach this problem differently.

Django allows us to use Low-level API to store any part of the page no matter how small it is.

To access this cache:

Use django.core.cache.caches. This is a dict-like object that gives you access to the caches configured n the CACHES settings.

Also, remember to provide the named key or you will get an InvalidCacheBackendError.

Conclusion

There is definitely more than meets the eye when dealing with Django Cache Framework. This framework is designed to give you more control over the kind of data you want to be stored for your visitors making it an ideal tool for building dynamic pages.

To make full use of this framework, take the time and go through the provided docs by Django. Ultimately, however, the kind of caching level you want will depend on the website you are making and the kind of services you offer. Not all caching levels will be ideal for your website.