2012-11-29 18 views
5

I持って次のモデル:防止O(N)クエリ

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

    def primary_group(self): 
     return self.memberships.select_related('group').get(is_primary=True) 

class Group(models.Model): 
    name = models.CharField() 
    members = models.ManyToManyField(Artist, through='Membership') 

class Membership(models.Model): 
    artist = models.ForeignKey(Artist, related_name='memberships') 
    group = models.ForeignKey(Group) 
    is_primary = models.BooleanField() 

ArtistGroupが仲介モデル、Membershipを介して連結されています。アーティストには、is_primary、有効化されたものなどのプライマリグループが1つしかありません。

私はアーティストをリストするテンプレートで、上記の方法で呼び出されるプライマリグループに加えて、基本的なアーティスト情報をリストします。しかし、これはO(n)操作であり、これを行うには約160人のアーティストがいます。次のようにジャンゴ・デバッグ・ツールバーが提供するSQLは、次のとおりです。

SELECT ••• FROM "people_membership" 
      LEFT OUTER JOIN "people_group" ON ("people_membership"."group_id" = "people_group"."id") 
      WHERE ("people_membership"."artist_id" = xx AND "people_membership"."is_primary" = true) 

は私がすべてのアーティストが記載されているため、この問題が発生したことを追加してみましょう、私は約160これらのを取得します。

私はモデルメソッドを呼んでいると考えて、O(n)を行うことができますか?それとも、これを改善するために何か他の方法がありますか?(非正規化の短さはprimary_group)?これは、ソースまたはターゲットのいずれかから呼び出したい中間モデルに格納されている情報の種類に問題があるようです。

あなたが簡単に任意の嫌いは何を言われてもかかわらず、全く問題ではない、2つのクエリでこれを行うことができます

答えて

6

artists = list(Artist.objects.all()) 
primary_memberships = {m.artist_id: m for m in Group.objects.filter(is_primary=True, membership__artist__in=artists).extra(select={'artist_id': '%s.artist_id' % (Membership._meta.db_table,)})} 
for artist in artists: 
    artist.primary_membership = primary_memberships.get(artist.id) 

を(追加の句が正しくないかもしれませんが、あなたのアイデアを得ます)

これに加えて、私は好きですし、主機能を変更します

if hasattr(self, '_primary_membership_cache'): 
    return self._primary_membership_cache 

そして、あなたが情報を添付した場合、その後、その変数にバインドして、ちょうどあなたの同じ機能のCAを使用ll。

(私たちは、すべての様々な加入/奇数クエリのディスカスの場所の上にパターンのこの種に従う)

+1

これは現在大部分が正しいと確信しています:) –

0

ではなく、アーティストよりも、あなたは会員でクエリを開始しようとしたことがありますか?

class Artist(models.Model): 
    ... 
    def primary_group(self): 
     return Membership.objects.filter(artist=self).get(is_primary=True).group 
+0

結果のクエリはほぼ同じです。 –

4

私はデビッド・クレイマーが言うようにそれを行う代わりに、余分なのでしょう:あなたは簡単にアーティストのいずれかのリストにそれを適用することができるようにボーナスポイント

primary_memberships = {m.artist_id: m.group for m in Membership.objects.filter(group__isprimary=True, artist__in=artists).select_related('group')} 
for artist in artists: 
    artists.primary_membership = primary_memberships.get(artist.id) 

会員のマネージャーにこの方法作ります!

1

membershipartist_id,is_primary)に2列のインデックスを付けるのはどうですか? If you've already upgraded to 1.5b1これはモデル内で行うことができますが、そうでない場合はバックエンドでこれを行うことを止めるものはありません。これによりメンバーシップの検索が一定の時間に短縮されるはずです。あなたのDBがそれをサポートしている場合は、それをpartial indexにすることができますが、160人のアーティストしか必要としません。

関連する問題