Django - Multiple Sites Site Caching

1.4k views Asked by At

I have a number of sites under one Django application that I would like to implement site wide caching on. However it is proving to be a real hassle.

what happens is that settings.CACHE_MIDDLEWARE_KEY_PREFIX is set once on startup, and I cannot go ahead and change it depending on what the current site is. As a result if a page of url http://website1.com/abc/ is cached then http://website2.com/abc/ renders the cached version of http://website1.com/abc/. Both these websites are running on the same Django instance as this is what Django Sites appears to allow us to do.

Is this an incorrect approach? Because I cannot dynamically set CACHE_MIDDLEWARE_KEY_PREFIX during runtime I am unable to cache multiple sites using Django's Site wide caching. I also am unable to do this for template and view caching.

I get the impression that the way this really needs to be setup is that each site needs its own Django instance which is pretty much identical except for the settings file, which in my case will differ only by the value of CACHE_MIDDLEWARE_KEY_PREFIX. These Django instances all read and write to the same database. This concerns me as it could create a number of new issues.

Am I going down the right track or am I mistaken about how multi site architecture needs to work? I have checked the Django docs and there is not real mention of how to handle caching (that isn't low level caching) for Django applications that serve multiple sites.

3

There are 3 answers

0
Shawn Chin On

(Disclaimer: the following is purely speculation and has not been tested. Consume with a pinch of salt.)

It might be possible to use the vary_on_headers view decorator to include the 'Host' header in the cache key. That should result in cache keys that include the HTTP Host header, thus effectively isolating the caches for your sites.

@vary_on_headers('Host')
def my_view(request):
    # ....

Of course, that will only work on a per-view basis, and having to add a decorator to all views can be a big hassle.

Digging into the source of @vary_on_headers reveals the use of patch_vary_headers() which one might be able to use in a middleware to apply the same behaviour on a site level. Something along the lines of:

from django.utils.cache import patch_vary_headers

class VaryByHostMiddleware(object):
    def process_response(self, request, response):
        patch_vary_headers(response, ('Host',))
        return response
1
tufla On

I faced this problem recently. What I did based on the documentation was to create a custom method to add the site id to the key used to cache the view.

In settings.py add the KEY_FUNCTION argument:

CACHES = {
    'default': {
        'BACKEND': 'path.to.backend',
        'LOCATION': 'path.to.location',
        'TIMEOUT': 60,
        'KEY_FUNCTION': 'path.to.custom.make_key_per_site',
        'OPTIONS': {
            'MAX_ENTRIES': 1000
        }
    }
}

And my custom make_key method:

def make_key_per_site(key, key_prefix, version):
    site_id = ''
    try:
        site = get_current_site() # Whatever you use to get your site's data
        site_id = site['id']
    except:
        pass
    return ':'.join([key_prefix, site_id, str(version), key])
0
Sergey Lyapustin On

You need to change get_full_path to build_absolute_uri in django.util.cache

def _generate_cache_header_key(key_prefix, request):
"""Returns a cache key for the header cache."""
#path = md5_constructor(iri_to_uri(request.get_full_path()))
path = md5_constructor(iri_to_uri(request.build_absolute_uri())) # patch using full path
cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
    key_prefix, path.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)


def _generate_cache_key(request, method, headerlist, key_prefix):
"""Returns a cache key from the headers given in the header list."""
ctx = md5_constructor()
for header in headerlist:
    value = request.META.get(header, None)
    if value is not None:
        ctx.update(value)
#path = md5_constructor(iri_to_uri(request.get_full_path()))
path = md5_constructor(iri_to_uri(request.build_absolute_uri()))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
    key_prefix, request.method, path.hexdigest(), ctx.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)

Or create you own slightly changed cache middleware for multisite. http://macrotoma.blogspot.com/2012/06/custom-multisite-caching-on-django.html