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)该字段为nullleft_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
相关产品推荐
相关产品推荐

