如何有效地在 django 中递归查询?

Posted

技术标签:

【中文标题】如何有效地在 django 中递归查询?【英文标题】:How to recursively query in django efficiently? 【发布时间】:2017-01-23 12:46:12 【问题描述】:

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

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)向 Admin 报告,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 最终会超时。有什么方法可以更有效地获取团队的所有成员?

【问题讨论】:

向我们展示您的 get_team() 方法 已添加到Q中。 您是否尝试过在外键上使用 _set 方法?我从来没有在递归模型上做过,所以不知道输出可能是什么样子。您也可以尝试 .select_related() 并查看最终结果。我认为这将为您提供必要的输出,但我不确定效率如何。 您应该使用一种有效的方式来存储/查询这些数据,例如github.com/django-mptt/django-mptt 【参考方案1】:

如果您使用的数据库支持递归公用表表达式(例如 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
''')

参考资料: Raw SQL queries in DjangoRecursive Common Table Expressions in PostgreSQL

【讨论】:

【参考方案2】:

我找到了解决问题的方法。递归解决方案采用节点,进入它的第一个子节点并深入到层次结构的底部。然后再次回到第二个孩子(如果存在),然后再次下降到底部。简而言之,它会一一探索所有节点并将所有成员附加到一个数组中。我想出的解决方案是逐层获取成员。

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

此方法所需的 db 调用数是我们想要找出其团队的成员下方存在的层数(层次结构)。

【讨论】:

您可以通过编写一个简单的递归查询来让数据库完成所有工作,如 Andrea 的回答所示。在您的情况下,您发出多个查询并将它们的结果附加在一起 ​​- 不错,但是当有一个纯基于数据库的解决方案时,为什么让它如此脆弱? :-)

以上是关于如何有效地在 django 中递归查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何在递归 SQL 查询中查找子树中的所有节点?

Django自递归外键过滤器查询所有孩子

sql server 递归查询

MySQL怎样做递归查询

ms sql 2005 递归查询如何实现

mysql递归查询