2016-09-23 19 views
6

私のジャンゴ - パワードアプリが正常に動作しているが、私は、データベースが実際のデータが取り込まれますよう、パフォーマンスの問題に遭遇し始めました。私はDjango Debug Toolbarでいくつかのプロファイリングを行い、多くのエンドポイントがデータを返す過程で数十から数百のクエリを発行することがわかりました。ジャンゴRESTフレームワーク:DRFのAPIを持つネストされたシリアライザのためのプリフェッチの設定

私は以前にデータベースクエリに関しては何も最適化されていなかったので、私は、このことを期待。しかし、私はプリフェッチを設定しているので、適切にプリフェッチされたシリアライザ・データを別のシリアライザにネストすると、そのシリアライザ・データを正しく使用することができません。私はこれをawesome postとして、さまざまなプリフェッチ方法について考える方法のガイドとして使用してきました。私は/api/readinggroups/エンドポイントを打ったとき

現在、私のReadingGroupシリアライザが適切にプリフェッチを行います。私の問題は/api/userbookstats/エンドポイントで、すべてUserBookStatsのオブジェクトを返します。関連するシリアライザUserBookStatsSerializerは、ネストされたReadingGroupSerializerを持っています。

モデル、シリアライザ、以下のようにビューセットは、次のとおり

models.py

class ReadingGroup(models.model): 
    owner = models.ForeignKeyField(settings.AUTH_USER_MODEL) 
    users = models.ManyToManyField(settings.AUTH_USER_MODEL) 
    book_type = models.ForeignKeyField(BookType) 
    .... 
    <other group related fields> 

    def __str__(self): 
    return '%s group: %s' % (self.name, self.book_type) 

class UserBookStats(models.Model): 
    reading_group = models.ForeignKey(ReadingGroup) 
    user = models.ForeignKey(settings.AUTH_USER_MODEL) 
    alias = models.CharField() 

    total_books_read = models.IntegerField(default=0) 
    num_books_owned = models.IntegerField(default=0) 
    fastest_read_time = models.IntegerField(default=0) 
    average_read_time = models.IntegerField(default=0) 

serializers.py

class ReadingGroupSerializer(serializers.ModelSerializer): 
    users = UserSerializer(many = True,read_only=True) 
    owner = UserSerializer(read_only=True) 

    class Meta: 
     model = ReadingGroup 
     fields = ('url', 'id','owner', 'users') 

    @staticmethod 
    def setup_eager_loading(queryset): 
     #select_related for 'to-one' relationships 
     queryset = queryset.select_related('owner') 

     #prefetch_related for 'to-many' relationships 
     queryset = queryset.prefetch_related('users') 

     return queryset 

class UserBookStatsSerializer(serializers.HyperlinkedModelSerializer): 
    reading_group = ReadingGroupSerializer() 
    user = UserSerializer() 
    awards = AwardSerializer(source='award_set', many=True) 

    class Meta: 
     model = UserBookStats 
     fields = ('url', 'id', 'alias', 'total_books_read', 'num_books_owned', 
       'average_read_time', 'fastest_read_time', 'awards') 

    @staticmethod 
    def setup_eager_loading(queryset): 
     #select_related for 'to-one' relationships 
     queryset = queryset.select_related('user') 

     #prefetch_related for 'to-many' relationships 
     queryset = queryset.prefetch_related('awards_set') 

     #setup prefetching for nested serializers 
     groups = Prefetch('reading_group', queryset ReadingGroup.objects.prefetch_related('userbookstats_set'))   
     queryset = queryset.prefetch_related(groups) 

     return queryset 

views.py

class ReadingGroupViewset(views.ModelViewset): 

    def get_queryset(self): 
    qs = ReadingGroup.objects.all() 
    qs = self.get_serializer_class().setup_eager_loading(qs) 
    return qs 

class UserBookStatsViewset(views.ModelViewset): 

    def get_queryset(self): 
    qs = UserBookStats.objects.all() 
    qs = self.get_serializer_class().setup_eager_loading(qs) 
    return qs 

私は(私は実際にそのエンドポイントhereため、重複クエリを排除について投稿)ReadingGroupエンドポイントのプリフェッチを最適化してきた、そして今、私はUserBookStatsエンドポイントに取り組んでいます。

私が抱えている問題は、現在のsetup_eager_loadingUserBookStatsSerializerで、ReadingGroupSerializerの熱心な読み込み方法で設定されたプリフェッチを使用していないようです。私はまだPrefetchオブジェクトのシンタックスには少しぼんやりしています - 私はそのアプローチを試してみるとよい答えがthisです。

明らかUserBookStatsViewsetget_queryset方法はReadingGroupのオブジェクトのsetup_eager_loadingを呼び出すことはありませんが、私は同じプリフェッチを達成するための方法があります確信しています。二重のアンダースコアの構文を使用して、内部の関係をプリフェッチ

+1

あなたはエレガントなソリューションか何か解決策について質問していますか? 'queryset = queryset.prefetch_related( 'reading_group'、 'reading_group__users'、 'reading_group__owner')'はうまくいくはずです。 – serg

+0

どのような解決策が素晴らしいだろう - とうわー、それは素晴らしいです。それは完璧に働くので、答えとして提出すれば、私は受け入れます。 Prefetchオブジェクトを置き換えます。これはプリフェッチのデカップルのような洗練されたソリューションに使用したいと考えていたものですが、これは本当にうまく動作します。ありがとう! – dkhaupt

答えて

3

prefetch_related()サポート:

queryset = queryset.prefetch_related('reading_group', 'reading_group__users', 'reading_group__owner') 

私はジャンゴRESTは、自動的にすべての必要なフィールドをフェッチするためのボックスのうちいずれかのエレガントなソリューションを提供していないと思います。

関連する問題