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.
Django comes with a robust cache system that lets you save dynamic pages so they don’t have to be calculated for each request. For convenience, Django offers different levels of cache granularity: You can cache the output of specific views, you can cache only the pieces that are difficult to produce, or you can cache your entire site.
Django comes with an optional “sites” framework. It’s a hook for associating objects and functionality to particular websites, and it’s a holding place for the domain names and “verbose” names of your Django-powered sites.
It’s a hook for associating objects and functionality to particular websites, and it’s a holding place for the domain names and “verbose” names of your Django-powered sites. Use it if your single Django installation powers more than one site and you need to differentiate between those sites in some way.
django.contrib.sites registers a post_migrate signal handler which creates a default site named example.com with the domain example.com. This site will also be created after Django creates the test database. To set the correct name and domain for your project, you can use a data migration.
(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
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])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With