问题是:我有一个博客应用程序,我缓存了5分钟的post输出视图。
@cache_page(60 * 5)
def article(request, slug):
...
然而,每当有新的评论添加到帖子中时,我想使缓存无效。我想知道如何最好地做到这一点?
我看过这个相关的问题,但它已经过时了。
这是我在搜索解决方案时的第一次尝试,而当前的答案并没有太大帮助,所以在仔细研究了Django的源代码后,我找到了这个答案。
是的,您可以通过编程方式知道密钥,但这需要一些工作。
Django的页面缓存通过引用request
对象来工作,特别是请求路径和查询字符串。这意味着对于具有不同查询字符串的每个页面请求,您将具有不同的缓存键。在大多数情况下,这不太可能是一个问题,因为你想要缓存/无效的页面将是一个已知的字符串,比如/blog/my-awesome-year
,所以要使其无效,你只需要使用Django的RequestFactory
:
from django.core.cache import cache
from django.test import RequestFactory
from django.urls import reverse
from django.utils.cache import get_cache_key
cache.delete(get_cache_key(RequestFactory().get("/blog/my-awesome-year")))
如果您的URL是一个固定的值列表(即没有不同的查询字符串),那么您可以到此为止。但是,如果您有很多不同的查询字符串(比如搜索页面的?q=xyz
),那么最好的选择可能是为每个视图创建一个单独的缓存。然后您可以将cache="cachename"
传递给cache_page()
,然后使用清除整个缓存
from django.core.cache import caches
caches["my_cache_name"].clear()
关于此策略的重要说明
它只适用于未经验证的页面。用户登录后,cookie数据就成为缓存密钥创建过程的一部分,因此以编程方式重新创建该密钥变得更加困难。我想你可以尝试从会话存储中提取cookie数据,但其中可能有数千个密钥,你必须使它们中的每一个失效/预缓存。
我会用一种不同的方式缓存:
def article(request, slug):
cached_article = cache.get('article_%s' % slug)
if not cached_article:
cached_article = Article.objects.get(slug=slug)
cache.set('article_%s' % slug, cached_article, 60*5)
return render(request, 'article/detail.html', {'article':cached_article})
然后将新评论保存到本文对象:
# ...
# add the new comment to this article object, then
if cache.get('article_%s' % article.slug):
cache.delete('article_%s' % article.slug)
# ...
深入代码后,很明显我们可以做到这一点,但我们需要有您想要无效的API URL(带有查询和头),这看起来有点不可能,但如果是的话,Django使用md5作为哈希键,您可以尝试使用与头完全相同的URL生成相同的哈希。
def _generate_cache_key(request, method, headerlist, key_prefix):
"""Return a cache key from the headers given in the header list."""
ctx = hashlib.md5()
for header in headerlist:
value = request.META.get(header)
if value is not None:
ctx.update(value.encode())
url = hashlib.md5(iri_to_uri(request.build_absolute_uri()).encode('ascii'))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
key_prefix, method, url.hexdigest(), ctx.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)
这看起来并不容易,然后你可以为你拥有的每个URL设置一个前缀,找到所有具有相同前缀的密钥,并逐一删除它们,这是一个更好的选择。
cache_page(DEFAULT_TIMEOUT, None, 'your-cache-prefix')
我不建议每次你想显示未缓存的数据时都更改查询参数,但是的,还有一种方法,当你想要新数据时,你可以不传递缓存头。它将在默认情况下工作。
只是一张纸条
头没有缓存,同时修改查询参数永远不会使实际的缓存无效。