Django QuerySet的真正缓存是什么?
根据(我对)官方dox的解读: 在计算Django查询集时,应将其缓存。但事实似乎并非如此。在下面的示例中,TrackingImport是一个模型,后面有一个非常大的表。(为简洁起见,对输出稍加编辑。) 调用len()似乎与宣传的一样有效:Django QuerySet的真正缓存是什么?,django,django-models,django-queryset,Django,Django Models,Django Queryset,根据(我对)官方dox的解读: 在计算Django查询集时,应将其缓存。但事实似乎并非如此。在下面的示例中,TrackingImport是一个模型,后面有一个非常大的表。(为简洁起见,对输出稍加编辑。) 调用len()似乎与宣传的一样有效: In [104]: len(recs) Out[104]: 1823 In [105]: time(recs[0]) Wall time: 0.00 s 我不明白为什么解引用数组没有缓存QuerySet结果。它必须对它进行评估,对吗?那么我遗漏了什么呢
In [104]: len(recs)
Out[104]: 1823
In [105]: time(recs[0])
Wall time: 0.00 s
我不明白为什么解引用数组没有缓存QuerySet结果。它必须对它进行评估,对吗?那么我遗漏了什么呢?您可以浏览源代码(django.db.model.query),然后您就会明白,这里是django 1.3.4的query.py
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_缓存。这就是为什么多个resc[0]的持续时间花费相同的时间
调用len(resc)后,可以找到源代码
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_缓存有值,然后调用recs[0],它只会使用缓存
if self._result_cache is not None:
....
return self._result_cache[k]
源代码从不说谎,因此当您在文档中找不到答案时,最好阅读源代码。感谢您提供了如此详尽的答案。
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)
if self._result_cache is not None:
....
return self._result_cache[k]