web-dev-qa-db-ja.com

複数のレベルのprefetch_related

モデルが次のようになっている場合:

_class Publisher(models.Model):
    pass

class Book(models.Model):
    publisher = models.ForeignKey(Publisher)

class Page(models.Model):
    book = models.ForeignKey(Book)
_

Publisherのクエリセットを取得したいのですが、Publisher.object.all()を実行します。その後、プリフェッチを確実にしたい場合は、次のことができます:

_Publisher.objects.all().prefetch_related('book_set')`
_

私の質問は:

  1. _select_related_を使用してこのプリフェッチを行う方法はありますか、または_prefetch_related_を使用する必要がありますか?
  2. _page_set_をプリフェッチする方法はありますか?これは動作しません:

Publisher.objects.all().prefetch_related('book_set', 'book_set_page_set')

33
Alex Rothberg
  1. いいえ、逆関係に_select_related_を使用することはできません。 _select_related_はSQL結合を行うため、メインクエリセットの単一レコードは、関連テーブル(ForeignKeyまたはOneToOneフィールド)の1つを正確に参照する必要があります。 _prefetch_related_は、実際には完全に別の2番目のクエリを実行し、結果をキャッシュしてから、それをPythonのクエリセットに「結合」します。そのため、ManyToManyまたは逆ForeignKeyフィールドに必要です。

  2. マルチレベルのプリフェッチを行うために2つのアンダースコアを試しましたか?このように:Publisher.objects.all().prefetch_related('book_set', 'book_set__page_set')

31
jproffitt

Django 1.7なので、Django.db.models.Prefetchクラスのインスタンスは.prefetch_relatedの引数として使用できます。Prefetchオブジェクトコンストラクタにはquerysetそのようにネストされた複数レベルのプリフェッチを指定できるようにする引数:

Project.objects.filter(
        is_main_section=True
    ).select_related(
        'project_group'
    ).prefetch_related(
        Prefetch(
            'project_group__project_set',
            queryset=Project.objects.prefetch_related(
                Prefetch(
                    'projectmember_set',
                    to_attr='projectmember_list'
                )
            ),
            to_attr='project_list'
        )
    )

ListQuerySetを使用してプリフェッチ結果を処理するため(フィルター/順序)、サフィックスが_listの属性に格納されます。

21
Dmitriy Sintsov