Django cache decorator per user

Django -- Posted on July 12, 2023

This is a Python decorator named `cache_per_user` that can be used to cache the view for each user. It allows you to cache the results of a view function based on different parameters, like cache lifetime, cache key prefix, and whether to cache POST requests.

Let's break down the decorator and its inner functions:

1. The `cache_per_user` decorator takes four optional parameters:
   - `ttl`: Cache lifetime in seconds (Time To Live). If not specified, the cache will last until the server restarts or removes it.
   - `prefix`: Prefix to be used in the cache key. If not provided, it will use 'view_cache_' followed by the function's name as the prefix.
   - `cache_post`: A boolean parameter that determines whether to cache POST requests. If set to `True`, POST requests will be cached, otherwise not.

2. The `decorator` function is the actual decorator returned by `cache_per_user`. It takes the view function as an argument and returns the `apply_cache` function.

3. The `apply_cache` function is the wrapper function that replaces the original view function. It takes the `request` object and any number of positional and keyword arguments (`*args` and `**kwargs`) that the original view function may accept.

4. The `apply_cache` function first checks whether the user is anonymous or authenticated. If the user is anonymous, the cache key will include '_anonymous', indicating that the cache is shared among all anonymous users. Otherwise, the user's ID will be included in the cache key, making the cache unique for each authenticated user.

5. Depending on whether a `prefix` was provided, the `base_key` for the cache is generated using the `prefix` and the user ID or 'anonymous'.

6. The `args` and `kwargs` (if any) are used to create additional parts of the cache key. These parameters are used to differentiate cache entries based on different function argument combinations.

7. The final `CACHE_KEY` is created by combining the `base_key`, `args_key`, and `kwargs_key`.

8. The decorator checks whether the view can be cached (`can_cache`) based on the `cache_post` setting and the request's method. If the `cache_post` is `False` and the request method is 'POST', the view response will not be cached.

9. If the response is not already present in the cache, the view function is called with the given arguments, and the response is stored in the cache using `cache.set()` with the specified `ttl` (time to live) if caching is allowed.

10. If the response is already present in the cache, it is returned directly from the cache, avoiding the need to recompute the response.

In summary, this decorator provides a way to cache views based on different parameters, enabling better control over caching behavior per user and view arguments. The caching mechanism used here appears to be based on a cache object (likely from a cache backend like Django's caching framework) accessed via `cache.get()` and `cache.set()`.

              
                def cache_per_user(ttl=None, prefix=None, cache_post=False):
    '''Decorator that caches the view for each user
        * ttl - Cache lifetime, not sending this parameter means the
          cache will last until the server restarts or decides to remove it
        * prefix - Prefix to be used to cache the response. if not
          be informed will use 'view_cache_'+function.__name__
        * cache_post - Informs whether to cache POST requests
        * Cache for anonymous users is shared with everyone
        * The cache key will be one of the possible options:
            '%s_%s'%(prefix, user.id)
            '%s_anonymous'%(prefix)
           'view_cache_%s_%s_%s_%s'%(function.__name__, user.id,args,kwargs)
            'view_cache_%s_anonymous'%(function.__name__)
    '''
    def decorator(function):
        def apply_cache(request, *args, **kwargs):
            if request.user.is_anonymous():
                user = 'anonymous'
            else:
                user = request.user.id

            if prefix:
                base_key = '%s_%s' % (prefix, user)
            else:
                base_key = 'view_cache_%s_%s' % (function.__name__, user)

            # Include function arguments in the cache key
            args_key = '_'.join([str(arg) for arg in args])

            # Include keyword arguments in the cache key
            kwargs_key = '_'.join(['%s=%s' % (key, value) for key, value in kwargs.items()])

            # Generate the cache key
            CACHE_KEY = '%s_%s_%s' % (base_key, args_key, kwargs_key)

            if not cache_post and request.method == 'POST':
                can_cache = False
            else:
                can_cache = True

            if can_cache:
                response = cache.get(CACHE_KEY, None)
            else:
                response = None

            if not response:
                response = function(request, *args, **kwargs)
                if can_cache:
                    cache.set(CACHE_KEY, response, ttl)
            return response

        return apply_cache

    return decorator
                  
   
            

Related Posts