如何缓存分页的Django查询集,特别是在ListView中?
我注意到一个查询需要很长时间才能运行,所以我试图缓存它。查询集非常大(超过100 k条记录),所以我尝试只缓存分页的子部分。我不能缓存整个视图或模板,因为有些部分是用户/会话特定的,需要不断更改。
ListView有两个标准方法来检索查询集,get_queryset()
,它返回非分页数据,paginate_queryset()
,它根据当前页面过滤数据。
我首先尝试在get_queryset()
中缓存查询,但很快意识到调用cache.set(my_query_key, super(MyView, self).get_queryset())
会导致整个查询被序列化。
然后我尝试重写paginate_queryset()
,如下所示:
import time
from functools import partial
from django.core.cache import cache
from django.views.generic import ListView
class MyView(ListView):
...
def paginate_queryset(self, queryset, page_size):
cache_key = 'myview-queryset-%s-%s' % (self.page, page_size)
print 'paginate_queryset.cache_key:',cache_key
t0 = time.time()
ret = cache.get(cache_key)
if ret is None:
print 're-caching'
ret = super(MyView, self).paginate_queryset(queryset, page_size)
cache.set(cache_key, ret, 60*60)
td = time.time() - t0
print 'paginate_queryset.time.seconds:',td
(paginator, page, object_list, other_pages) = ret
print 'total objects:',len(object_list)
return ret
然而,这几乎需要一分钟的时间来运行,即使只有10个对象被检索,并且每个请求都显示“re-caching”,这意味着没有任何东西被保存到缓存中。
我的settings.CACHE
看起来像:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
service memcached status
显示memcached正在运行,而tail -f /var/log/memcached.log
什么都没有显示。
我做错了什么?什么是正确的方式来缓存分页查询,使整个查询集是不是检索?
编辑:我认为这可能是memcached或Python Package 器中的一个bug。Django似乎支持两种不同的memcached后端,一种使用python-memcached,另一种使用pylibmc。python-memcached似乎隐藏了缓存paginate_queryset()
值的错误。当我切换到pylibmc后端时,现在我得到一个显式的错误消息“error 10 from memcached_set:服务器错误”追溯到django/core/cache/backends/memcached.py in set,line 78.
4条答案
按热度按时间pcrecxhr1#
您可以扩展
Paginator
以支持由提供的cache_key
进行缓存。关于这种
CachedPaginator
的使用和实现的博客文章可以在here上找到。源代码发布在djangosnippets.org上(这里是web-acrhive link,因为原始代码不工作)。不过,我将发布一个从原始版本稍微修改的示例,它不仅可以缓存每页的对象,还可以缓存总计数。(有时甚至计数也是一项昂贵的操作)。
p3rjfoxz2#
问题原来是多种因素综合作用的结果。主要是,
paginate_queryset()
返回的结果包含对无限查询集的引用,这意味着它本质上是不可访问的。当我调用cache.set(mykey, (paginator, page, object_list, other_pages))
时,它试图序列化数千条记录,而不是我期望的page_size
数量的记录,导致缓存项超出memcached的限制并失败。另一个因素是在memcached/python-memcached中可怕的默认错误报告,它默默地隐藏了所有错误,并在出现错误时将cache.set()转换为nop,这使得跟踪问题非常耗时。
我通过重写
paginate_queryset()
来修复这个问题,以完全放弃Django的内置分页器功能,并自己计算查询集:然后缓存那个
object_list
laximzn53#
我想在我的主页上对我的无限滚动视图进行分页,这是我想出的解决方案。它是Django CCBV和作者的初始解决方案的混合。
响应时间,然而,并没有改善,因为我希望,但这可能是因为我测试它对我的本地只有6个职位和2个用户哈哈。
bcs8qyzn4#
下面是如何使用Todor的answer在
ListView
中缓存分页的解释。假设您的应用程序中有多个ListView
。它们中的每一个都需要自己不同的cache_key
。添加paginator_class = CachedPaginator
并通过父类覆盖get_paginator
函数。