Issue
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.
Solution
The default cache_page
decorator accepts a variable called key_prefix
. However, it can be passed as a string parameter only. So you can write your own decorator, that will dynamically modify this prefix_key
based on the is_authenticated
value. Here is an example:
from django.views.decorators.cache import cache_page
def cache_on_auth(timeout):
def decorator(view_func):
@wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
return cache_page(timeout, key_prefix="_auth_%s_" % request.user.is_authenticated())(view_func)(request, *args, **kwargs)
return _wrapped_view
return decorator
and then use it on the view:
@cache_on_auth(60*60)
def myview(request)
Then, the generated cache_key will look like:
cache key:
views.decorators.cache.cache_page._auth_False_.GET.123456.123456
if the user is authenticated, and
cache key:
views.decorators.cache.cache_page._auth_True_.GET.789012.789012
if the user is not authenticated.
Answered By - Tisho
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.