2016-08-12 10 views
1

私は人が受け取った賞を追跡するDjangoアプリケーションを作成しています。以下は、私が持っている二つのモデルの単純化した表現です:Djangoのクエリ数を減らす

class AwardHolder(models.Model): 
    name = models.CharField() 

    def get_total_awards(self): 
     entries = self.award_set.all() 
     calc = entries.aggregate(sum=Sum('units_awarded')) 
     return calc.get('sum') or 0 


class Award(models.Model) 
    date_awarded = models.DateField() 
    units_awarded = models.IntegerField() 
    award_holder = models.ForeignKey(AwardHolder) 

私は彼が彼の合計の賞を見ることができます賞ホルダーのための概要ページを作成しました。上記のget_total_awards機能を使用します。それはすべてうまくいきますが、私は概要表を作成して受賞者1人あたりの賞を表示しました。それはすべてのループデシベルを打つため、

def get_all_awards(): 
    qs = AwardHolder.objects.all() 
    awards = [] 
    for ah in qs: 
     awards.append((ah, ah.get_total_awards()) 
    return awards 

これは、クエリを大量に生成します。私は、賞ホルダー当たりの総賞を取得するには、以下の機能を使用します。 prefetch_relatedやその他のdbトリックを使用してget_all_awards()を書き直すことなく、dbクエリの数を減らす方法はありますか?

私が使用する実際のコードは、この例よりもはるかに多くのフィールドを持ち、より複雑です。 get_all_awards()に似た10の機能がありますので、書き直すことはかなりの作業になります。しかし、100人の受賞者のために私のコードは18000件の質問を生成していました。

+1

に注釈を付けるためにモデルマネージャを使用してください。 'AwardHolder.objects.annotate(Count( 'award__units_awarded')))のようなもの' –

+0

私はたくさんの機能をやり直さなければならないので、これを避けることを望んでいました。私はAwardHolder内のget_total_awards関数は、単一の受賞者の賞の数だけが必要な場合はまだ適用されるだろうと思いますか? – Johan

+1

単一の受賞者の場合、関数が完全に完璧なのは、何とかその1つのクエリが少ない場合を除きます:P 大きな数値の場合は、ループしたり複数のクエリを実行するのではなく、私がそれがしていると思うことを実際に行います –

答えて

1

は、私は、これは注釈ではなく、集計のための仕事だと思う常にAwardHolders

class AwardHolderQuerySet(models.QuerySet): 
    def all(self): 
     return self.annotate(Count('award__units_awarded')) 

class AwardHolder(models.Model): 
    name = models.CharField() 
    objects = AwardHolderQuerySet.as_manager() 
    ... 
1

get_all_awardsは必ずしもget_total_awardsに電話する必要はありません。 Djangoがすでに提供している機能を使用する代わりに、各エンティティに対して同じクエリを繰り返します。annotate

get_all_awardsを変更してannotateを使用し、get_total_awardsを使用すると、1つのエンティティに特典が適用された場合にのみ使用できます。

def get_all_awards(): 
    qs = AwardHolder.objects.annotate(sum=Sum('award__units_awarded')) 
    awards = [] 
    for ah in qs: 
     awards.append((ah, ah.sum)) 
    return awards 

あなたもforループをドロップすると、クエリセットを直接qsからの結果を使用することができます。

各エンティティごとに個別のクエリを実行するのではなく、複数のオブジェクトとその関連オブジェクトをフェッチしているときに、最適化されたクエリの利点が得られます。

関連する問題