2012-12-31 22 views
2

根據(我讀的)這裏的官方的強力黴素:什麼_really_緩存一個Django查詢集?

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

當你評估它一個Django的QuerySet應緩存。但似乎並非如此。在下面的示例中,TrackingImport是一個具有非常大表格的模型。 (輸出略加編輯,以便簡潔)

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()似乎標榜的工作:

In [104]: len(recs) 
Out[104]: 1823 

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

我不知道爲什麼提領該陣列沒有緩存查詢集的結果。它必須評估它,對吧?那麼我錯過了什麼?

回答

7

你可以通過源代碼(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_cache是​​無,那麼當你調用RESC [0],它只會跳到下面幾行,

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 [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_cache具有價值,那麼你調用區域經濟共同體[0],它只會使用緩存,

if self._result_cache is not None: 
     .... 
    return self._result_cache[k] 

源代碼永遠不會說謊,所以最好在你沒有在文檔中找到你的答案時閱讀源代碼。

+1

不錯的挖! + –

+0

謝謝你這樣一個徹底的答案。 – shanusmagnus