96

このモデルがあるとします:

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 つのクエリのみを実行します (1 つはアルバムを取得するためのもので、もう 1 つは `SELECT * IN photos 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でそれらをフィルタリングできることに気付くほど賢くありませんか? ドキュメントのどこかで、それを行うことになっていることを誓います...

4

3 に答える 3

183

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()の動作を制御できるオブジェクトがありますprefetch_related

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ドキュメントを参照してください。

于 2012-10-19T12:58:48.713 に答える