【问题标题】:Django - vary_on_cookie or user cache results in misses for anonymous usersDjango - vary_on_cookie 或用户缓存导致匿名用户未命中
【发布时间】:2015-11-04 07:09:30
【问题描述】:

我正在尝试在某些特定报告页面上实现基于文件的按视图缓存。我将特别关注一个名为 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,每个用户都会在页面的右上角看到原始用户的信息(即“电子邮件地址 | 注销链接”)。匿名用户也是如此,这就是问题的根源。

我找到了上述解决方案。正如所写,它的工作原理是它为每个用户或匿名创建一个新的页面缓存版本。这对每个人来说都很棒

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

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

所以我的问题是这样的:

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

或者,是否有更好的方法来实现这种按视图缓存来绕过这个问题并达到相同的目的? (即不管身份验证如何缓存,但不共享用户特定数据)。

谢谢!

【问题讨论】:

  • 在深入研究用户代理想法的任何细节之后,看起来这是一个标题字符串,它引用经过身份验证的用户对象或为该匿名创建的 AnonymousUser 对象实例。所以我的问题是它在整个唯一实例上有所不同。但是,所有这些实例都将具有相同的“id”属性设置为“None”。答案可能是“不是没有自定义装饰器”,但是有没有办法根据这个对象的属性而不是整个实例来改变缓存?

标签: django python-2.7 caching python-decorators


【解决方案1】:

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

我没有找到解决这个特定问题的方法(如果有人有的话,我很想听听以了解更多信息!),但我找到了一个可以接受的解决方法。

我最初考虑使用自定义模板标签,但不值得编写编译和渲染函数之类的东西,尤其是当这些 per-view 装饰器看起来如此简单时。

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

在这样做的过程中,我偶然发现了:https://github.com/twidi/django-adv-cache-tag

这使我可以创建自定义模板标签并指定默认以外的后端。

这并不理想,我想写一个类似这样的东西,它只是创建一个新的 {% adv_cache %} 标签,它接受选项而不是接管默认的 {% cache %} 标签,从而无需使用扩展它编写自定义标签来调用这个东西的方法。

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


更新:

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

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

我将很快向 pypi 注册,以便于安装。

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

我还演示了如何使用 django

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

这会引发“缓存”没有属性或方法 get_cache() 的错误。对于 1.7+,他们在这个库中添加了“缓存”,因此导入更容易,无需任何方法调用:

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

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

此外,我想演示在缓存标记类的上下文之外使用它们的密钥生成功能。我认为这个包的一个缺陷是使这些类方法只有在你启动它的实例时才能被调用......据我所知,这是无法做到的(或者至少你不能进行虚假的“test-cache = CacheTag()”调用以创建一个虚拟实例并访问其方法)。

我将 django-adv-cache-tag 将缓存键制作成更具逻辑性的“generate_cache_key()”函数进行了抽象,该函数可全局访问。如果用户提交了更新报告的更正,这对于手动使某些页面上的缓存无效至关重要。相关报告需要更新,而这种长期缓存与之不兼容。

我只是添加了一个失效功能。然后,在您更改数据的任何视图中,您都可以使用 generate_cache_key() 来生成该页面可能存在的任何键(我循环遍历我的 vary_on 参数的所有排列),并调用 invalidate_filecache() 函数来核对这些页面.

也就是说,在 1.8+ 中更容易,因为所有这些都包含在本机 {% cache %} 标记中(后端键现在是默认参数)和新的 django.core.cache您可以导入 .cache 以在视图中使用多个后端。

但是,如果您像我一样坚持使用 1.8 之前编写的遗留代码,我希望这可以帮助您,这样您就不必经历我所做的整个过程来弄清楚什么应该是真正简单的缓存模板的变化(显然 django 核心开发人员同意,因为这是作为默认功能添加的)。

【讨论】:

    猜你喜欢
    • 2014-02-08
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 2011-01-06
    相关资源
    最近更新 更多