如何高效地在Django中进行递归查询?

20

我有一个模型,它看起来像这样:

class StaffMember(models.Model):

    id = models.OneToOneField(to=User, unique=True, primary_key=True, related_name='staff_member')
    supervisor = models.ForeignKey(to='self', null=True, blank=True, related_name='team_members')

我的团队当前的层级结构是这样设计的:有一个管理员(位于最高层次)。现在,假设3个人(A、B、C)向管理员汇报工作,每个人都有自己的团队向他们汇报工作以此类推。

我想要查找任何员工的所有团队成员(归纳到层级结构的底部),我的当前方法是:

def get_team(self):
    team = [self]
    for c in self.team_members.all():
        team += list(c.get_team())
        if len(team) > 2000:
            break
    return team

我可以通过以下方式获取成员的团队成员:

member = StaffMember.objects.get(pk=72)
team = member.get_team()

显然,这会导致大量的数据库调用,最终导致我的API超时。有什么更有效的方法可以获取一个团队的所有成员?


你尝试过在外键上使用_set方法吗?我从未在递归模型上这样做过,所以不知道输出会是什么样子。你也可以尝试使用.select_related(),看看最终的结果如何。我认为这将给你必要的输出,但效率如何我不确定。 - Keith Bailey
3
你应该使用一种高效的方式来存储/查询这些数据,比如https://github.com/django-mptt/django-mptt。 - Daniel Roseman
2个回答

30
如果您使用支持递归公共表达式(例如 PostgreSQL)的数据库,那么这正是使用情况。
team = StaffMember.objects.raw('''
    WITH RECURSIVE team(id, supervisor) AS (
          SELECT id, supervisor 
          FROM staff_member
          WHERE id = 42
        UNION ALL
          SELECT sm.id, sm.supervisor
          FROM staff_member AS sm, team AS t
          WHERE sm.id = t.supervisor
        )
    SELECT * FROM team
''')

参考文献: 在Django中使用原始SQL查询
PostgreSQL中的递归公共表达式


3

我找到了一个解决问题的方法。递归解决方案需要获取节点,进入其第一个子节点并一直向下到层次结构的底部。然后再返回到第二个子节点(如果存在),然后再次向下到底部。简而言之,它逐个探索所有节点,并将所有成员附加到数组中。我提出的解决方案以分层方式获取成员。

member = StaffMember.objects.get(id__id=user_id)

new_list = [member]

new_list = get_final_team(new_list)

def get_final_team(qs):
    team = []
    staffmembers = StaffMember.objects.filter(supervisor__in=qs)

    team += staffmembers 
    if staffmembers:
        interim_team_qs = get_final_team(staffmembers)
        for qs in interim_team_qs:
            team.append(qs)
    else:
        team = [qs]

    return team

这个方法涉及的数据库调用次数取决于我们想要查找团队成员下面有多少层(层级)。


3
你可以像Andrea的回答所示那样编写一个简单的递归查询,让数据库来完成所有工作。在你的情况下,你正在发出多个查询并将它们的结果附加在一起--这并不坏,但为什么要让它变得如此脆弱,当有一个纯粹基于数据库的解决方案呢? :-) - ankush981

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接