Question
In Django, how can create a single cached version of a page (same for all users) that's only visible to authenticated users?
Setup
The pages I wish to cache are only available to authenticated users (they use @login_required
on the view). These pages are the same for all authenticated users (e.g. no need to setup vary_on_headers
based on unique users).
However, I don't want these cached pages to be visible to non-authenticated users.
What I've tried so far
- Page level cache (shows pages intended for logged in users to non-logged in users)
- Looked into using
vary_on_headers
, but I don't need individually cached pages for each user - I checked out template fragment caching, but unless I'm confused, this won't meet my needs
- Substantial searching (seems that everyone wants to do the reverse)
Thanks!
Example View
@login_required
@cache_page(60 * 60)
def index(request):
'''Display the home page'''
return render(request, 'index.html')
settings.py (relevant portion)
# Add the below for memcache
MIDDLEWARE_CLASSES += (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
)
# Enable memcache
# https://devcenter.heroku.com/articles/memcache#using_memcache_from_python
CACHES = {
'default': {
'BACKEND': 'django_pylibmc.memcached.PyLibMCCache'
}
}
Solution
Based on the answer by @Tisho I solved this problem by
- Creating a
decorators.py
file in my app - Adding the below code to it
- Importing the function in
views.py
- Applying it as a decorator to the views I wanted to cache for logged in users only
decorators.py
from functools import wraps
from django.views.decorators.cache import cache_page
from django.utils.decorators import available_attrs
def cache_on_auth(timeout):
def decorator(view_func):
@wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return cache_page(timeout)(view_func)(request, *args, **kwargs)
else:
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
For logged in users, it would cache the page (or serve them the cached page) for non-logged in users, it would just give them the regular view, which was decorated with @login_required
and would require them to login.
The default
cache_page
decorator accepts a variable calledkey_prefix
. However, it can be passed as a string parameter only. So you can write your own decorator, that will dynamically modify thisprefix_key
based on theis_authenticated
value. Here is an example:and then use it on the view:
Then, the generated cache_key will look like:
if the user is authenticated, and
if the user is not authenticated.
If the @wrap decorator in the @Tisho answer makes your brain hurt, or if an explicit solution is better than an implicit one, here's a simple procedural way to serve different cache results:
I'd advice against using the cache middleware if you want fine tunning of your caching abilities.
However, if you do want to persist keeping it, you could try something like (not saying it would work as is, but something similar to it):
Worst case scenario, you can use cache.set('view_name', template_rendering_result), and cache.get, to just cache the HTML manually.