私はこのモデルを持っていると仮定します:
class PhotoAlbum(models.Model):
title = models.CharField(max_length=128)
author = models.CharField(max_length=128)
class Photo(models.Model):
album = models.ForeignKey('PhotoAlbum')
format = models.IntegerField()
アルバムのサブセット内の写真のサブセットを効率的に見たい場合。私はこのようなことをします:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
これは2つのクエリのみを実行します。これは、アルバムを取得するクエリと、「SELECT * IN写真WHERE photoalbum_id IN()」のようなクエリです。
すべてが素晴らしいです。
しかし、私がこれを行うと:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
次に、WHERE format = 1
!を使用して大量のクエリを実行します。私は何か間違っているのですか?Djangoはすでにすべての写真を取得してPythonでそれらをフィルタリングできることに気付くのに十分ではありませんか?それ...
Django 1.6以前では、余分なクエリを避けることはできません。prefetch_related
呼び出しは、クエリセット内のすべてのアルバムのa.photoset.all()
の結果を効果的にキャッシュします。ただし、a.photoset.filter(format=1)
は異なるクエリセットであるため、すべてのアルバムに対して追加のクエリを生成します。
これは prefetch_related
のドキュメントで説明されています。 filter(format=1)
はfilter(spicy=True)
と同等です。
代わりにpythonで写真をフィルタリングすることにより、数またはクエリを減らすことができます。
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = [p for p in a.photo_set.all() if p.format == 1]
Django 1.7)には、prefetch_related
の動作を制御できる Prefetch()
オブジェクトがあります。
from Django.db.models import Prefetch
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related(
Prefetch(
"photo_set",
queryset=Photo.objects.filter(format=1),
to_attr="some_photos"
)
)
for a in someAlbums:
somePhotos = a.some_photos
Prefetch
オブジェクトの使用方法のその他の例については、 prefetch_related
のドキュメントをご覧ください。
docs から:
... QuerySetsと同様に、異なるデータベースクエリを意味する後続のチェーンメソッドは、以前にキャッシュされた結果を無視し、新しいデータベースクエリを使用してデータを取得します。したがって、次のように書くと:
pizzas = Pizza.objects.prefetch_related('toppings')
[list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
...その後、pizza.toppings.all()がプリフェッチされているという事実は役に立ちません。実際、使用していないデータベースクエリを実行したため、パフォーマンスが低下します。そのため、この機能は注意して使用してください!
あなたの場合、「a.photo_set.filter(format = 1)」は新しいクエリのように扱われます。
さらに、「photo_set」は逆ルックアップであり、まったく別のマネージャーを介して実装されます。