5

私は次のモデルを持っています:

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ます。アーティストは、1つのプライマリグループのみを持つことができます。これは、を介してマークされis_primary、検証されます。

アーティストを一覧表示するテンプレートでは、上記の方法で呼び出されたプライマリグループに加えて、基本的なアーティスト情報を一覧表示します。ただし、これはO(n)操作であり、これを行うには約160人のアーティストがいます。django-debug-toolbarが提供する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)?これは、ソースまたはターゲットのいずれかから呼び出したい中間モデルに格納されているあらゆる種類の情報に問題があるようです。

4

4 に答える 4

6

これは、2つのクエリで簡単に実行できます。これは、嫌いな人が言うことにもかかわらず、まったく問題ではありません。

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

次に、情報を添付する場合は、その情報をその変数にバインドし、同じ関数呼び出しを使用します。

(さまざまな結合/奇数クエリについて、DISQUSの至る所でこの種のパターンに従います)

于 2012-11-29T02:21:39.380 に答える
4

私はDavidCramerが言うようにそれをしますが、余分なものではありません:

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)

ボーナスポイントについては、これをメンバーシップのマネージャーの方法にして、アーティストのリストに簡単に適用できるようにしてください。

于 2012-11-29T02:40:04.170 に答える
1

membershipartist_idis_primary) に2列のインデックスを付けるのはどうですか?すでに1.5b1にアップグレードしている場合は、モデル内でこれを行うことができますが、そうでない場合は、バックエンドでこれを行うことを妨げるものは何もありません。これにより、メンバーシップの検索が一定時間に短縮されます。DBがそれをサポートしている場合は、部分インデックスにすることができますが、アーティストが160人しかないため、それほど必要ではないようです。

于 2012-11-29T02:42:20.523 に答える
0

アーティストではなく、メンバーシップでクエリを開始してみましたか?

class Artist(models.Model):
    ...
    def primary_group(self):
        return Membership.objects.filter(artist=self).get(is_primary=True).group
于 2012-11-29T02:24:06.103 に答える