Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Caching for Authenticated Users Only

Tags:

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

  1. Creating a decorators.py file in my app
  2. Adding the below code to it
  3. Importing the function in views.py
  4. 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.

like image 293
Jeff Avatar asked Jul 26 '12 02:07

Jeff


People also ask

What types of caching mechanisms are Django supported?

Django also works well with “downstream” caches, such as Squid and browser-based caches. These are the types of caches that you don't directly control but to which you can provide hints (via HTTP headers) about which parts of your site should be cached, and how.

What mechanism is used to enable caching for an individual view in Django?

Memcached. This is the most efficient caching system supported natively in Django. Memcached provides a fast interface for adding, retrieving, and deleting data from the cache. Here, all data are stored directly in memory instead of the database, which makes accessing the data faster.

Does Django automatically cache?

Local Memory Cache Unless we explicitly specify another caching method in our settings file, Django defaults to local memory caching. As its name implies, this method stores cached data in RAM on the machine where Django is running. Local memory caching is fast, responsive, and thread-safe.

Can you briefly outline a couple of caching approaches for Django?

To use cache in Django, first thing to do is to set up where the cache will stay. The cache framework offers different possibilities - cache can be saved in database, on file system or directly in memory. Setting is done in the settings.py file of your project.


2 Answers

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.

like image 84
Tisho Avatar answered Oct 14 '22 22:10

Tisho


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:

from django.views.decorators.cache import cache_page  def index(request):     """     :type request: HttpRequest     """     is_authenticated = request.user.is_authenticated()     if is_authenticated:         return render_user(request)     else:         return render_visitor(request)  @cache_page(5, key_prefix='user_cache') def render_user(request):     print 'refreshing user_cache'     return render(request, 'home-user.html', {})  @cache_page(10, key_prefix='visitor_cache') def render_visitor(request):     print 'refreshing visitor_cache'     return render(request, 'home-visitor.html', {}) 
like image 43
Bryce Avatar answered Oct 14 '22 23:10

Bryce