仅针对经过身份验证的用户的 Django 缓存
Posted
技术标签:
【中文标题】仅针对经过身份验证的用户的 Django 缓存【英文标题】:Django Caching for Authenticated Users Only 【发布时间】:2012-07-24 13:54:33 【问题描述】:问题
在 Django 中,如何创建仅对经过身份验证的用户可见的页面的单个缓存版本(所有用户都相同)?
设置
我希望缓存的页面仅对经过身份验证的用户可用(他们在视图上使用@login_required
)。这些页面对于所有经过身份验证的用户都是相同的(例如,无需根据唯一用户设置 vary_on_headers
)。
但是,我不希望未经身份验证的用户可以看到这些缓存页面。
到目前为止我已经尝试过什么
页面级缓存(显示用于已登录用户和未登录用户的页面) 考虑使用vary_on_headers
,但我不需要为每个用户单独缓存页面
我检查了模板片段缓存,但除非我感到困惑,否则这无法满足我的需求
大量搜索(似乎每个人都想反其道而行之)
谢谢!
示例视图
@login_required
@cache_page(60 * 60)
def index(request):
'''Display the home page'''
return render(request, 'index.html')
settings.py(相关部分)
# 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'
解决方案
根据@Tisho 的回答,我解决了这个问题
-
在我的应用中创建
decorators.py
文件
添加以下代码
导入views.py
中的函数
将其作为装饰器应用到我希望仅为登录用户缓存的视图中
装饰器.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
对于已登录的用户,它会为未登录的用户缓存页面(或为他们提供缓存的页面),它只会为他们提供常规视图,用@login_required
装饰并要求他们登录.
【问题讨论】:
下面没有中间件解决方案。 【参考方案1】:默认的cache_page
装饰器接受一个名为key_prefix
的变量。但是,它只能作为字符串参数传递。因此,您可以编写自己的装饰器,它将根据 is_authenticated
值动态修改此 prefix_key
。这是一个例子:
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
然后在视图上使用它:
@cache_on_auth(60*60)
def myview(request)
然后,生成的 cache_key 将如下所示:
cache key:
views.decorators.cache.cache_page._auth_False_.GET.123456.123456
如果用户通过了身份验证,并且
cache key:
views.decorators.cache.cache_page._auth_True_.GET.789012.789012
如果用户未通过身份验证。
【讨论】:
感谢您的回答。我承认,我很惊讶 Django 没有“内置”的方式来实现这一点。也许他们将来会添加它。 是的,可能值得添加。实际上 Django 只提供了基本功能,而扩展它们确实很容易。我刚刚看到答案中缺少一些东西 - 为未经身份验证的用户禁用缓存 - 只是return view_func(request, *args, **kwargs)
而不应用 cache_page
装饰器。我觉得很容易根据实际需要修改。
5 年后,非常有帮助。谢谢Tisho!
我不知道我是不是做错了什么,但是我必须在这个装饰器之前添加never_cache
才能在Django 2.2中正常工作。
请注意,在 Django 1.10+ 中,is_authenticated
是一个属性,而不是一个函数。所以你需要把request.user.is_authenticated()
部分改成request.user.is_authenticated
***.com/a/3644910【参考方案2】:
如果你想微调你的缓存能力,我建议不要使用缓存中间件。
但是,如果您确实想坚持保留它,您可以尝试类似的方法(不是说它会按原样工作,而是类似于它):
@never_cache
def dynamic_index(request):
# do dynamic stuff
def cached_index(request):
return dynamic_index(request)
@never_cache
def index(request):
if request.user.is_authenticaded():
return cached_index(request)
return dynamic_index(request)
最坏的情况,您可以使用cache.set('view_name', template_rendering_result)
和cache.get
手动缓存HTML。
【讨论】:
【参考方案3】:如果@Tisho 答案中的@wrap 装饰器让您的大脑受伤,或者如果显式解决方案比隐式解决方案更好,这里有一个提供不同缓存结果的简单程序方法:
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', )
【讨论】:
【参考方案4】:我知道这是一个非常古老的问题,但我们有一个新的替代方案来解决这个问题。
您可以使用装饰器cache_control 将private
传递为True
来保护您的数据。
【讨论】:
这不会为所有经过身份验证的用户共享相同的缓存值,这是此处所要求的。以上是关于仅针对经过身份验证的用户的 Django 缓存的主要内容,如果未能解决你的问题,请参考以下文章