【问题标题】:Cache a django view that has URL parameters缓存具有 URL 参数的 django 视图
【发布时间】:2010-12-08 02:28:25
【问题描述】:

我最近实现了 Django 出色的缓存框架。但是据我了解,Django 不会缓存在获取请求中传递参数的视图。 我有一个 Ajax 视图,它传递了我想要缓存 X 秒的参数,有什么简单的方法可以做到这一点?

在伪代码中,我目前有一个 URL:

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

我想缓存任何这个 url,只要它具有相同的 get 参数。

我目前在我的视图中使用缓存装饰器:

myview(stuff)

myview = cache_page(myview, 60 * 3)

我确实读过关于 django's vary headers 的文章,但它让我有点不知所措,我什至不确定它是否是正确的解决方案

【问题讨论】:

  • 有什么原因你不想只使用:http://mysites/ajaxthing/user/foo/items/10/ 这样你就可以很好地避开这个问题。
  • 请阅读下面euqidron的回答,这种行为在Django 1.3中已经改变了。
  • 这个问题的前提是“Django 不会缓存在 get 请求中传递参数的视图”,但根据 Kyle 在下面的回答,这不是真的。 Django 在缓存键中包含查询参数。

标签: python django memcached


【解决方案1】:

没错,variable headers 不是正确的解决方案,当你想根据用户代理等客户端请求头缓存时使用它。

您需要使用low-level API 或template fragment caching。这真的取决于你的看法。

使用低级 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
    ...
    ..
    .

【讨论】:

  • +1 用于模板片段缓存。它在某些情况下非常有用。
  • 感谢简洁的大纲,真的很有用。
【解决方案2】:

有点晚了,但你可以使用django-view-cache-utils。

【讨论】:

    【解决方案3】:

    是的,您可以使用 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):
        ...
    

    【讨论】:

    • 这种方法很好,但我们遇到了代码的两个主要问题,1. 具有相同 URL 参数的两个视图可能存在冲突,2. str(request.GET) 可能返回不同,因为字典条目显示没有指定顺序。后来我改用了这段代码:@cache_page_with_prefix(60*15, lambda request: md5_constructor(request.build_absolute_uri()).hexdigest())。
    • @roman-dolgiy django2 中 md5_constructor 的等价物是什么?
    【解决方案4】:

    这在 Django 1.3+ 中应该不再是问题。 见:https://docs.djangoproject.com/en/dev/topics/cache/#using-vary-headers

    【讨论】:

    • 你的回答离题了,因为问题是关于 URL 参数的。与请求标头无关。
    【解决方案5】:

    看起来您不再需要做任何比将@cache_page([时间长度]) 放在您尝试缓存的视图函数上方更复杂的事情,无论您在 URL 中是否有参数。

    例如,如果您有这样的网址:

    http://example.com/user/some_user_id
    

    views.py 中的视图函数如下所示:

    from django.views.decorators.cache import cache_page
    ...
    
    @cache_page(60 * 10)
    def get_user_detail(request, user_id=None):
        ...
        return render(...)
    

    【讨论】:

    • 我试过这个,但在使用通用视图时从 urls.py 得到一个 AttributeError:url(r'^(?P[0-9]+)/teams/(?P[0-9]+)/detail/$', views.TeamDetailView.as_view(), name='team_detail'), AttributeError: 'function' object has no attribute 'as_view'
    【解决方案6】:

    根据我对源代码的阅读和经验测试,@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(),其中“中间件”是django.middleware.cache.CacheMiddleware。
    5. 调用django.utils.cache.get_cache_key() 生成缓存键。
    6. 调用django.utils.cache._generate_cache_header_key()。
    7. 调用request.build_absolute_uri(),其中“请求”为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 参数的变化而变化。

    【讨论】:

    • 我可以确认这一点。 @cache_page 装饰器本机正确处理查询字符串参数 (?param1=value1&param2=value2)。如果查询字符串相同,则使用缓存。在 Django 3.2.9 中测试。
    猜你喜欢
    • 2014-12-22
    • 2011-04-12
    • 1970-01-01
    • 2011-12-12
    • 2013-08-18
    • 2018-09-04
    • 2011-01-11
    • 2012-04-24
    • 2012-03-10
    相关资源
    最近更新 更多