2

ここでの公式のdox(の私の読書)によると:

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

配列の逆参照がQuerySetの結果をキャッシュしなかった理由がわかりません。それを評価しなければなりませんでしたね?だから私は何が欠けていますか?

4

1 に答える 1

7

ソースコード(django.db.model.query)に目を通すと、明確になります。これがdjango1.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はNoneになり、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に値があることがわかります。次に、recs [0]を呼び出すと、キャッシュが使用されます。

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

ソースコードは決して嘘をつかないので、ドキュメントに答えが見つからない場合はソースコードを読むことをお勧めします。

于 2012-12-31T01:08:34.997 に答える