缓存带有URL参数的Django视图

16 投票
5 回答
11893 浏览
提问于 2025-04-15 15:11

我最近实现了Django的缓存框架,这个框架非常不错。不过我了解到,Django不会缓存那些在GET请求中带有参数的视图。

我有一个Ajax视图,它会接收GET参数,我想把这个视图缓存X秒,想知道有什么简单的方法可以做到这一点。

现在我有一个这样的URL:

http://mysites/ajaxthing/?user=foo&items=10

只要这个URL的GET参数相同,我希望能够缓存它。

我现在在我的视图中使用了缓存装饰器:

myview(stuff)

myview = cache_page(myview, 60 * 3)

我也看过关于Django的变异头部的内容,但我觉得有点难懂,而且我也不确定这是否是正确的解决方案。

5 个回答

9

根据我对源代码的阅读和实际测试,@cache_page这个装饰器在处理GET参数时表现得很好(至少在Django 2.2版本中是这样)。

深入看看源代码:

  1. 这个装饰器是在django.views.decorators.cache里定义的。
  2. 它调用了django.utils.decorators.decorator_from_middleware_with_args()
  3. 接着又调用了django.utils.decorators.make_middleware_decorator()
  4. 这个过程有点复杂,像洋葱一样一层一层的函数返回函数。重要的是,它调用了middleware.process_request(),这里的'middleware'指的是django.middleware.cache.CacheMiddleware
  5. 然后它调用django.utils.cache.get_cache_key()来生成缓存的键。
  6. 接着又调用了django.utils.cache._generate_cache_header_key()
  7. 然后调用了request.build_absolute_uri(),这里的'request'是django.http.request.HttpRequest
  8. 接下来调用了django.http.request.HttpRequest.get_full_path()
  9. 然后又调用了django.http.request.HttpRequest._get_full_path()
  10. 最后,它会在返回的字符串中包含self.META.get('QUERY_STRING', '')

另一方面,当响应完成时,类似的过程会通过middleware.process_response()进行,最终调用django.utils.cache._generate_cache_header_key()来决定将响应存储在缓存中的位置。

通过实际观察,我可以看到对这个装饰器修饰的视图的请求是被缓存的,并且当GET参数变化时,响应也会相应变化。

9

是的,你可以使用django-view-cache-utils,下面是适合你情况的代码:

from view_cache_utils import cache_page_with_prefix
from django.utils.hashcompat import md5_constructor
...
@cache_page_with_prefix(60*15, lambda request: md5_constructor(request.get_full_path()).hexdigest())
def my_view(request):
    ...
20

没错,改变头部信息并不是正确的解决办法,这种方法是用来根据客户端请求的头部信息(比如用户代理等)来进行缓存的。

你需要使用低级API或者模板片段缓存。具体使用哪种方法其实取决于你的视图。

使用低级API时,代码大概是这样的:

from django.core.cache import cache

def get_user(request):
    user_id = request.GET.get("user_id")
    user = cache.get("user_id_%s"%user_id)
    if user is None:
        user = User.objects.get(pk=user_id)
        cache.set("user_id_%s"%user_id, user, 10*60) # 10 minutes
    ...
    ..
    .

撰写回答