Django 1.11:为经过身份验证的用户禁用缓存
Django 1.11: disable cache for authentificated users
我们有一个用 python 2.7 和 django 1.11 编写的遗留应用程序(没有要迁移的资源)。它还使用 grappelli
进行授权。我们尝试为某些页面添加 Edit
links(每个页面显示关于 Round
对象的详细信息),这些页面应该只对具有权限的授权用户 可见编辑 Round(grappelli
网络界面中的 APPNAME | round | Can change round
)。在模板中,权限检查如下:
{% if perms.round.can_change_round %}
 <a href="{{link_to_change_round}}" class="stuff-only-link">{% trans 'Edit' %}</a>
{% endif %}
当以下事件在很短的时间间隔内发生时,就会出现问题:
- 有权编辑回合的用户访问页面 - 并看到
Edit
link。
- 无权编辑回合的用户(例如匿名用户)访问相同的页面 - 并且还会看到 link!
相关设置 (settings.py
) 是:
CACHES = {
'default': {
# 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
SOLO_CACHE = 'default'
SOLO_CACHE_TIMEOUT = 5*60
当我将缓存更改为 dummy
时,问题就消失了。因此,完全禁用授权用户的缓存似乎是一个显而易见的解决方案。更准确地说:
a) 如果用户是匿名的(大多数真实站点用户)——请求的页面可以写入缓存,也可以从缓存中读取;
b) 如果一个用户被授权(大约 5-7 个用户)- 请求的页面不能写入缓存,也不能从缓存中读取。
如何实现?
您可以对模板的缓存部分进行分段,并从缓存中省略与用户相关的部分,或者根据分段变量状态将其缓存为 documented
非常感谢@Melvin 提供文档链接。经过一个小时的谷歌搜索后,找到并调整了答案。代码是:
编辑:最初,缓存是基于函数的。
因此,“/rounds/1”给出了与“/rounds/2”相同的(缓存的)值。
我们将完整的 URL 添加到缓存键以解决问题。
# -*- encoding: utf-8 -*-
'''
Python >= 2.4
Django >= 1.0
Author: eu@rafaelsdm.com
'''
# https://djangosnippets.org/snippets/2524/
#
#
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
然后在 views.py
:
from cache_per_user import cache_per_user as cache_page
#...
#
#
@cache_page(cache_duration)
def round_detail(request, pk):
我们有一个用 python 2.7 和 django 1.11 编写的遗留应用程序(没有要迁移的资源)。它还使用 grappelli
进行授权。我们尝试为某些页面添加 Edit
links(每个页面显示关于 Round
对象的详细信息),这些页面应该只对具有权限的授权用户 可见编辑 Round(grappelli
网络界面中的 APPNAME | round | Can change round
)。在模板中,权限检查如下:
{% if perms.round.can_change_round %}
 <a href="{{link_to_change_round}}" class="stuff-only-link">{% trans 'Edit' %}</a>
{% endif %}
当以下事件在很短的时间间隔内发生时,就会出现问题:
- 有权编辑回合的用户访问页面 - 并看到
Edit
link。 - 无权编辑回合的用户(例如匿名用户)访问相同的页面 - 并且还会看到 link!
相关设置 (settings.py
) 是:
CACHES = {
'default': {
# 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
SOLO_CACHE = 'default'
SOLO_CACHE_TIMEOUT = 5*60
当我将缓存更改为 dummy
时,问题就消失了。因此,完全禁用授权用户的缓存似乎是一个显而易见的解决方案。更准确地说:
a) 如果用户是匿名的(大多数真实站点用户)——请求的页面可以写入缓存,也可以从缓存中读取;
b) 如果一个用户被授权(大约 5-7 个用户)- 请求的页面不能写入缓存,也不能从缓存中读取。
如何实现?
您可以对模板的缓存部分进行分段,并从缓存中省略与用户相关的部分,或者根据分段变量状态将其缓存为 documented
非常感谢@Melvin 提供文档链接。经过一个小时的谷歌搜索后,找到并调整了答案。代码是:
编辑:最初,缓存是基于函数的。 因此,“/rounds/1”给出了与“/rounds/2”相同的(缓存的)值。 我们将完整的 URL 添加到缓存键以解决问题。
# -*- encoding: utf-8 -*-
'''
Python >= 2.4
Django >= 1.0
Author: eu@rafaelsdm.com
'''
# https://djangosnippets.org/snippets/2524/
#
#
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
然后在 views.py
:
from cache_per_user import cache_per_user as cache_page
#...
#
#
@cache_page(cache_duration)
def round_detail(request, pk):