Using django's site-wide cache middleware is pretty straight forward and acts as expected on a wagtail site. But what about per-view caching?
As it turns out, using django's per-view caching is quite easy to implement on wagtail Page sub-classes. Out of the box you can just do:
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
from wagtail.wagtailcore.models import Page
@method_decorator(cache_page(300), name="serve")
class MyPage(Page):
# my class code here
You can also add optional keyword arguments for 'key_prefix' and 'cache' to the 'cache_page' decorator. This gives quite a lot of flexibility and control over where a view is cached and what it's cache_key will be based on.
But what if you wanted to have a dynamic 'key_prefix', say based on the MyPage primary key? Well then you would need access to the instance of MyPage (self) and you can't do this with 'method_decorator'.
I wrote a decorator (based on 'method_decorator') that works for wagtail's Page and its sub-classes:
from django.views.decorators.cache import cache_page
def wagtail_cache_page(timeout, cache="", key_prefix="", append_key_prefix_with_pk=False):
def inner(obj):
"""
obj can be a class or method.
Must be either a wagtail Page subclass or
the 'serve' method of a wagtail Page subclass
"""
is_class = isinstance(obj, type)
if is_class:
if hasattr(obj, 'serve'):
serve = getattr(obj, 'serve')
if not callable(serve):
raise TypeError("Cannot decorate 'serve' as it isn't a callable ")
else:
raise ValueError("The Class must have a 'serve' method. ")
else:
serve = obj
def wrapper(self, request, *args, **kwargs):
suffix = str(self.pk) if append_key_prefix_with_pk else ''
@cache_page(timeout, cache=cache, key_prefix=key_prefix + suffix)
def _cache_page_hack(request, self, *args, **kwargs):
return serve(self, request, *args, **kwargs)
return _cache_page_hack(request, self, *args, **kwargs)
if is_class:
setattr(obj, 'serve', wrapper)
return obj
return wrapper
return inner
This will append the instance primary key to the 'key_prefix', which can be quite handy when trying to retrieve a cache_key for a specific page instance. You could tweak it to add any other info from the instance, if you wanted to.
You use it like this:
from my_app.decorators import wagtail_cache_page
from wagtail.wagtailcore.models import Page
@wagtail_cache_page(60*10, cache="my_cache", key_prefix="MyPage", append_key_prefix_with_pk=True)
class MyPage(Page):
# class stuff here