We have a legacy application written in python 2.7 and django 1.11 (and no resources to migrate). Also it uses grappelli
for authorization. We tried to add Edit
links for some pages (each of the pages displays detailed info on a Round
object) that should be visible only for authorized users with rights to edit a Round ( APPNAME | round | Can change round
in the grappelli
web interface). In the template, the permission is checked like so:
{% if perms.round.can_change_round %}
 <a href="{{link_to_change_round}}" class="stuff-only-link">{% trans 'Edit' %}</a>
{% endif %}
The problem occurs when the following events take place in a short time interval:
Edit
link.Revelant settings (settings.py
) are:
CACHES = {
'default': {
# 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
SOLO_CACHE = 'default'
SOLO_CACHE_TIMEOUT = 5*60
When I change cache to dummy
, the problem disappears. Thus, it seems to be an obvious solution to totally disable caching for authorized users. To be more precise:
a) If a user is anonymous (the most of real site users) - the requested page can be written to the cache and can be readed from the cache;
b) If a user is authorized (about 5-7 users) - the requested page can NOT be written to the cache and can NOT be readed from the cache.
How do I achieve this?
Many thanks to @Melvin for the links to the documentation. After an hour of googling, an answer was found and adapted. The code is:
EDIT: Originally, the cache was function-based. So, "/rounds/1" gave the same (cached) value as "/rounds/2". We add the full URL to the cache key to fix the problem.
# -*- encoding: utf-8 -*-
'''
Python >= 2.4
Django >= 1.0
Author: eu@rafaelsdm.com
'''
# https://djangosnippets.org/snippets/2524/
# https://stackoverflow.com/questions/20146741/django-per-user-view-caching
# https://stackoverflow.com/questions/62913281/django-1-11-disable-cache-for-authentificated-users
from django.core.cache import cache
def cache_per_user(ttl=None, prefix=None):
'''Decorador que faz cache da view pra cada usuario
* ttl - Tempo de vida do cache, não enviar esse parametro significa que o
cache vai durar até que o servidor reinicie ou decida remove-lo
* prefix - Prefixo a ser usado para armazenar o response no cache. Caso nao
seja informado sera usado 'view_cache_'+function.__name__
* cache_post - Informa se eh pra fazer cache de requisicoes POST
* O cache para usuarios anonimos é compartilhado com todos
* A chave do cache será uma das possiveis opcoes:
'%s_%s'%(prefix, user.id)
'%s_anonymous'%(prefix)
'view_cache_%s_%s'%(function.__name__, user.id)
'view_cache_%s_anonymous'%(function.__name__)
'''
def decorator(function):
def apply_cache(request, *args, **kwargs):
# No caching for authorized users:
# they have to see the results of their edits immideately!
can_cache = request.user.is_anonymous() and request.method == 'GET'
# Gera a chave do cache
if prefix:
CACHE_KEY = '%s_%s'%(prefix, 'anonymous')
else:
CACHE_KEY = 'view_cache_%s_%s_%s'%(function.__name__, request.get_full_path(), 'anonymous')
if can_cache:
response = cache.get(CACHE_KEY, None)
else:
response = None
if not response:
print 'Not in cache: %s'%(CACHE_KEY)
response = function(request, *args, **kwargs)
if can_cache:
cache.set(CACHE_KEY, response, ttl)
return response
return apply_cache
return decorator
and then in views.py
:
from cache_per_user import cache_per_user as cache_page
#...
#
#
@cache_page(cache_duration)
def round_detail(request, pk):