我有以下型号:
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()
Artist
和Group
通过中间模型Membership
进行关联。艺术家只能有一个主要小组,通过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
)?这似乎是存储在中间模型中的任何信息的问题,我想从源或目标中调用。
答案 0 :(得分: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
然后,如果您附加信息,请将其绑定到该变量,并使用相同的函数调用。
(我们在DISQUS各处遵循这种模式进行各种连接/奇数查询)
答案 1 :(得分:4)
我会像David Cramer所说的那样做,而不是额外的:
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)
奖励积分使其成为会员经理的一种方法,以便您轻松将其应用于任何艺术家名单!
答案 2 :(得分:1)
如何在membership
(artist_id
,is_primary
)上添加两列索引? If you've already upgraded to 1.5b1你可以在你的模型中做到这一点,但如果你没有,那么没有什么能阻止你在后端做这件事。这应该将成员资格查找减少到恒定时间。如果您的数据库支持它,您可以将其设为partial index,但只有160位艺术家,这似乎并非一切。
答案 3 :(得分:0)
您是否尝试使用成员资格而不是艺术家来启动查询?
class Artist(models.Model):
...
def primary_group(self):
return Membership.objects.filter(artist=self).get(is_primary=True).group