首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么_really_缓存了一个Django QuerySet?

什么_really_缓存了一个Django QuerySet?
EN

Stack Overflow用户
提问于 2012-12-31 08:48:02
回答 1查看 844关注 0票数 2

根据(我对)官方dox的解读:

https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated

在评估Django QuerySet时,它应该被缓存。但事实似乎并非如此。在下面的示例中,TrackingImport是一个模型,后面有一个非常大的表。(为简洁起见,对输出进行了稍微编辑。)

代码语言:javascript
复制
recs = TrackingImport.objects.filter(...stuff...)

In [102]: time(recs[0])
Wall time: 1.84 s

In [103]: time(recs[0])
Wall time: 1.84 s

调用len()看起来像广告中宣传的那样工作:

代码语言:javascript
复制
In [104]: len(recs)
Out[104]: 1823

In [105]: time(recs[0])
Wall time: 0.00 s

我不明白为什么取消对数组的引用没有缓存QuerySet结果。它必须对其进行评估,对吗?那么我错过了什么呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-12-31 09:08:35

你可以浏览一下源代码(django.db.model.query),然后你就会明白了,这是django 1.3.4的query.py,

代码语言:javascript
复制
def __getitem__(self, k):
    """
    Retrieves an item or slice from the set of results.
    """
    if not isinstance(k, (slice, int, long)):
        raise TypeError
    assert ((not isinstance(k, slice) and (k >= 0))
            or (isinstance(k, slice) and (k.start is None or k.start >= 0)
                and (k.stop is None or k.stop >= 0))), \
            "Negative indexing is not supported."

    if self._result_cache is not None:
        if self._iter is not None:
            # The result cache has only been partially populated, so we may
            # need to fill it out a bit more.
            if isinstance(k, slice):
                if k.stop is not None:
                    # Some people insist on passing in strings here.
                    bound = int(k.stop)
                else:
                    bound = None
            else:
                bound = k + 1
            if len(self._result_cache) < bound:
                self._fill_cache(bound - len(self._result_cache))
        return self._result_cache[k]

    if isinstance(k, slice):
        qs = self._clone()
        if k.start is not None:
            start = int(k.start)
        else:
            start = None
        if k.stop is not None:
            stop = int(k.stop)
        else:
            stop = None
        qs.query.set_limits(start, stop)
        return k.step and list(qs)[::k.step] or qs
    try:
        qs = self._clone()
        qs.query.set_limits(k, k + 1)
        return list(qs)[0]
    except self.model.DoesNotExist, e:
        raise IndexError(e.args)

如果您不遍历查询集,则_result_cache为None,然后当您调用resc时,它将跳到以下行,

代码语言:javascript
复制
try:
   qs = self._clone()
   qs.query.set_limits(k, k + 1)
   return list(qs)[0]
except self.model.DoesNotExist, e:
   raise IndexError(e.args)

您会发现,在本例中,没有设置_result_cache。这就是为什么多个resc的持续时间花费相同的时间。

在调用len(resc)之后,您可以找到源代码,

代码语言:javascript
复制
def __len__(self):
    # Since __len__ is called quite frequently (for example, as part of
    # list(qs), we make some effort here to be as efficient as possible
    # whilst not messing up any existing iterators against the QuerySet.
    if self._result_cache is None:
        if self._iter:
            self._result_cache = list(self._iter)
        else:
            self._result_cache = list(self.iterator())
    elif self._iter:
        self._result_cache.extend(self._iter)
    return len(self._result_cache)

您可以看到_result_cache具有值,然后调用recs,它将只使用缓存,

代码语言:javascript
复制
 if self._result_cache is not None:
         ....
     return self._result_cache[k]

源码从来不会说谎,所以当你在文档中找不到答案时,最好阅读源码。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14096279

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档