22

问题

在 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 的回答,我解决了这个问题

  1. decorators.py在我的应用程序中创建文件
  2. 将以下代码添加到它
  3. 将函数导入到views.py
  4. 将其作为装饰器应用于我只想为登录用户缓存的视图

装饰器.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并要求他们登录。

4

4 回答 4

29

默认cache_page装饰器接受一个名为key_prefix. 但是,它只能作为字符串参数传递。所以你可以编写自己的装饰器,它会prefix_key根据is_authenticated值动态修改它。这是一个例子:

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

如果用户未通过身份验证。

于 2012-07-28T19:22:25.640 回答
2

如果@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', {})
于 2014-01-10T02:35:50.930 回答
0

如果您想微调缓存能力,我建议您不要使用缓存中间件。

但是,如果您确实想坚持保留它,您可以尝试类似的方法(不是说它会按原样工作,而是类似于它):

@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。

于 2012-07-28T19:31:18.173 回答
-1

我知道这是一个非常古老的问题,但我们有一个新的替代方案来解决这个问题。

您可以使用装饰器cache_control传递privateasTrue来保护您的数据。

于 2020-05-20T14:07:53.500 回答