You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django用户推荐二叉树:数据库存储与15层级查询问题

二叉树推荐关系的数据库存储与层级查询实现(Django)

一、修正数据库模型设计

你当前的模型存在逻辑混淆问题,应该把二叉树节点与用户一一绑定,明确父节点(推荐人)和子节点(被邀请人)的关系。调整后的模型如下:

from django.contrib.auth.models import User
from django.db import models

class BinaryTreeNode(models.Model):
    # 一对一绑定系统用户,确保每个用户对应唯一二叉树节点
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
    # 当前用户的推荐人(父节点),根节点无推荐人
    referrer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='invited')
    # 当前用户邀请的左子节点
    left_child = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='left_parent')
    # 当前用户邀请的右子节点
    right_child = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='right_parent')

模型说明

  • user:通过一对一关联确保用户与二叉树节点一一对应,避免数据冗余
  • referrer:标记用户的推荐来源,对应二叉树的父节点,根节点(如示例中的A)该字段为null
  • left_child/right_child:存储当前用户邀请的两个子节点,若用户邀请人数超过2,可在业务层按规则分配左右节点(比如按邀请时间顺序)

二、15层级子树查询实现

Django ORM原生不支持递归查询,针对固定15层级的需求,提供两种可行方案:

方案1:内存递归遍历(适合中小数据量)

通过Python递归函数遍历二叉树,控制遍历深度不超过15层:

def get_subtree(root_user, max_depth=15):
    subtree_users = []
    root_node = BinaryTreeNode.objects.get(user=root_user)

    def traverse(node, current_depth):
        if not node or current_depth > max_depth:
            return
        # 将当前节点的用户加入结果集
        subtree_users.append(node.user)
        # 递归遍历左、右子节点
        traverse(node.left_child, current_depth + 1)
        traverse(node.right_child, current_depth + 1)

    traverse(root_node, 1)
    return subtree_users

使用示例

# 查询根节点A的15层级子树
a_user = User.objects.get(username='A')
a_subtree = get_subtree(a_user)

# 查询节点B的15层级子树
b_user = User.objects.get(username='B')
b_subtree = get_subtree(b_user)

方案2:数据库递归CTE查询(适合大数据量)

如果使用MySQL 8.0+或PostgreSQL等支持递归CTE的数据库,可通过原生SQL实现高效的层级查询,避免内存遍历的性能瓶颈:

def get_subtree_sql(root_user_id, max_depth=15):
    # MySQL递归CTE查询语句
    sql = """
    WITH RECURSIVE subtree AS (
        SELECT bt.user_id, bt.left_child_id, bt.right_child_id, 1 AS depth
        FROM yourapp_binarytreenode bt
        WHERE bt.user_id = %s
        UNION ALL
        SELECT child.user_id, child.left_child_id, child.right_child_id, parent.depth + 1 AS depth
        FROM yourapp_binarytreenode child
        JOIN subtree parent ON (child.user_id = parent.left_child_id OR child.user_id = parent.right_child_id)
        WHERE parent.depth < %s
    )
    SELECT user_id FROM subtree;
    """
    from django.db import connection
    with connection.cursor() as cursor:
        cursor.execute(sql, [root_user_id, max_depth])
        user_ids = [row[0] for row in cursor.fetchall()]
    # 返回查询到的用户对象列表
    return User.objects.filter(id__in=user_ids)

使用示例

a_subtree = get_subtree_sql(a_user.id)

注意:将yourapp_binarytreenode替换为你的Django应用名+模型表名(默认是应用名小写加模型名小写)。

三、可选优化方案

如果不需要严格的二叉树左右节点区分,仅需记录推荐关系并查询层级,可以简化模型:删除left_child和right_child字段,仅保留referrer。此时查询子树只需递归查找所有referrer为当前节点用户的子节点,业务层可根据需求调整展示结构,这种方式更灵活,支持用户邀请任意数量的人。

内容的提问来源于stack exchange,提问作者saugat timsina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 22:13:15