Django - vary_on_cookie或用户缓存导致匿名用户遗失

时间:2015-08-11 21:26:53

标签: django python-2.7 caching python-decorators

我正在尝试在某些特定报告页面上实现基于文件的每视图缓存。我将特别关注一个例子,称为ScorecardSummary。

在urls.py中:

url(r'^(?P<institution_slug>[^/]+)/report/(?P<submissionset>[^/]+)/$',
    ScorecardSummary.as_view(), name='scorecard-summary'),

在views.py中:

class ScorecardSummary(ScorecardView):
    template_name = 'institutions/scorecards/summary.html'

为了实现缓存,我改变了视图:

class ScorecardSummary(ScorecardView):
    template_name = 'institutions/scorecards/summary.html'

    @method_decorator(vary_on_cookie)
    @method_decorator(cache_page(86400 * 30, cache="filecache"))
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_anonymous():
            response = super(ScorecardSummary, self).dispatch(request, *args, **kwargs)
            patch_cache_control(response, public=True)
        else:
            response = super(ScorecardSummary, self).dispatch(request, *args, **kwargs)
            patch_cache_control(response, private=True)

        return response

我在方法之前和使用patch_cache_control()或patch_vary_headers()的逻辑中尝试过这些装饰器的一些不同的排列。

如果没有vary_on_cookie,每个用户都会在页面的顶角看到原始用户的信息(即“电子邮件地址|注销链接”)。匿名用户也是如此,这就是问题开始的地方。

我找到了上述解决方案。如上所述,这适用于为每个用户或匿名创建页面的新缓存版本。这对每个

都很棒

我的问题是,每当anon访问页面时,无论现有的缓存文件如何,它都会创建一个新的缓存,而不是加载现有的缓存,这会破坏目的。如果我将其修复为匿名缓存,那么我们将回复给每个用户提供完全相同的版本,我就回到我开始的地方。

我也尝试过使用vary_on_headers('User-Agent'),但它仍然区分每个匿名用户,而不是将它们视为相同。将private或public设置为True或不设置似乎没有效果。

所以我的问题是:

是否存在这些装饰器的一些排列以控制缓存,以便缓存BOTH身份验证和匿名,并且如果匿名用户已经触发了缓存的创建,则该版本将被提供给所有匿名而不是总是丢失。 / p>

或者,是否有更好的方法来实现这种绕过此问题并实现相同目的的每视图缓存? (即无论身份验证而不共享用户特定数据,都进行缓存。)

谢谢!

1 个答案:

答案 0 :(得分:0)

更新:此答案底部的更多信息

我没有找到这个特定问题的解决方案(如果有人有我喜欢听到它来了解更多信息!),但我找到了一个可接受的解决方法。

我最初考虑过一个自定义模板标签,但是不值得编写编译和渲染函数和东西,特别是当这些每个视图装饰器看起来更简单时。

我重新审视了这个想法,因为显然只有缓存页面的MOST,省略了顶部标题块,这是理想的选择。然后,可以向所有用户提供相同的缓存,而不会有包含用户特定数据的风险。

这样做我不小心偶然发现:https://github.com/twidi/django-adv-cache-tag

这允许我创建自定义模板标记并指定非默认的后端。

这不太理想,我想编写类似这样的内容,只需创建一个新的{%adv_cache%}标记,该标记采用选项而不是接管默认的{%cache%}标记,从而消除需要通过编写自定义标签来扩展它,只需要调用这个东西的方法。

无论如何,这是我的两分钱,我可能会在未来做一些更自动化的事情来替换它,或者看看修改本机缓存标记以接受参数来指定非默认后端。

UPDATE:

我的最终解决方案确实需要使用django-adv-cache-tag并创建自定义模板标记。由于这可能是一个非常常见的问题,并且该软件包的文档非常缺乏易于理解的示例,因此我编写了自己的软件包来扩展它:

https://github.com/baronvonvaderham/django-file-cache-tag

我很快就会用pypi进行注册,以便于安装。

以这样的方式编写,您可以非常轻松地安装它,修改一些settings.py行,然后直接使用标记。我认为它是一个可读的示例,可以用作模型,使用自己的自定义标记进一步扩展它,以用于除基于文件之外的其他后端。

我还演示了如何使用django&lt; 1.8的多个后端与django缓存API进行交互,因为我发现这些早期版本的官方文档实际上是不准确和自相矛盾的。示例:它表示通过以下方式加载特定缓存:

from django.core.cache import cache
my_cache = cache.get_cache('backend-name-from-settings')

这会抛出一个“缓存”的错误。没有属性或方法get_cache()。对于1.7+,他们添加了#34;缓存&#34;到这个库,所以导入工作更容易,无需任何方法调用:

from django.core.cache import caches
my_cache = caches['backend-name-from-settings']

易。只需访问相应的dict键即可。

此外,我想演示在缓存标记类的上下文之外使用其密钥生成函数。我认为这个包的一个缺陷是制作那些类方法,这样只有在你启动它的一个实例时才能调用它们......就我所知,这是不可能完成的(至少你可以& #39; t伪造&#34; test-cache = CacheTag()&#34;调用make一个虚拟实例并访问它的方法。)

我抽象了django-adv-cache-tag使缓存键成为更符合逻辑的方式&#34; generate_cache_key()&#34;全球可访问的功能。如果用户提交了更新报告的更正,则对于手动使某些页面上的缓存无效至关重要。相关报告需要更新,这种长期缓存与此不相容。

我只是添加了一个失效函数。然后,无论你有什么视图改变数据,你都可以使用generate_cache_key()来为该页面创建任何键(我循环遍历我的vary_on参数的所有排列),并调用invalidate_filecache()函数核对这些页面。

也就是说,它在1.8+中变得更容易了,因为所有这些都包含在原生{%cache%}标记中(后端密钥现在是默认参数),以及新的django您可以导入.core.cache.caches以在视图中使用多个后端。

但如果你像我一样并且坚持使用1.8之前编写的遗留代码,我希望这可以帮助你,所以你不必经历我所做的全部过程,以确定应该是什么模板中一个非常简单的缓存变体(显然django核心开发者同意,因为这是作为默认功能添加的。)