You need to enable JavaScript to run this app. Django ORM结合select_related、prefetch_related实现递归家谱查询
优惠活动
大模型
产品
解决方案
定价
更多

Django ORM结合select_related、prefetch_related实现递归家谱查询

Django ORM 家谱树查询实现方案

前置模型定义

首先基于你提到的三类模型给出参考关联定义,可根据实际业务字段调整:

from django.db import models

class Grandpa(models.Model):
    name = models.CharField(max_length=50)
    age = models.IntegerField()
    # 其余业务字段自定义
    class Meta:
        db_table = "grandpa"

class Father(models.Model):
    name = models.CharField(max_length=50)
    age = models.IntegerField()
    grandpa = models.ForeignKey(Grandpa, on_delete=models.CASCADE, related_name="fathers")
    # 其余业务字段自定义
    class Meta:
        db_table = "father"

class Son(models.Model):
    name = models.CharField(max_length=50)
    age = models.IntegerField()
    father = models.ForeignKey(Father, on_delete=models.CASCADE, related_name="sons")
    # 其余业务字段自定义
    class Meta:
        db_table = "son"

方案1:固定三级结构专用实现(性能最优)

基于Django ORM的Prefetch对象实现分层过滤,全程仅执行3次SQL查询,无N+1性能问题,优先推荐使用:

from django.db.models import Prefetch

def get_family_tree(grandpa_filters=None, father_filters=None, son_filters=None):
    # 初始化各层级默认过滤规则为空
    grandpa_filters = grandpa_filters or {}
    father_filters = father_filters or {}
    son_filters = son_filters or {}

    # 预定义Son层查询,应用Son级过滤规则
    son_queryset = Son.objects.filter(**son_filters)
    # 预定义Father层查询,应用Father级过滤规则,同时预加载符合条件的Son
    father_queryset = Father.objects.filter(**father_filters).prefetch_related(
        Prefetch("sons", queryset=son_queryset, to_attr="filtered_sons")
    )
    # 查询Grandpa层,应用Grandpa级过滤规则,同时预加载符合条件的Father
    grandpa_queryset = Grandpa.objects.filter(**grandpa_filters).prefetch_related(
        Prefetch("fathers", queryset=father_queryset, to_attr="filtered_fathers")
    )

    # 组装返回树形结构
    family_tree = []
    for grandpa in grandpa_queryset:
        grandpa_item = {
            "id": grandpa.id,
            "name": grandpa.name,
            "level": "grandpa",
            "children": []
        }
        for father in grandpa.filtered_fathers:
            father_item = {
                "id": father.id,
                "name": father.name,
                "level": "father",
                "children": []
            }
            for son in father.filtered_sons:
                son_item = {
                    "id": son.id,
                    "name": son.name,
                    "level": "son"
                }
                father_item["children"].append(son_item)
            grandpa_item["children"].append(father_item)
        family_tree.append(grandpa_item)
    return family_tree

调用示例

比如查询年龄大于70的爷爷、年龄在40-50岁之间的父亲、仍在上学的儿子(假设Son模型有is_student字段),调用方式如下:

tree = get_family_tree(
    grandpa_filters={"age__gt": 70},
    father_filters={"age__range": (40, 50)},
    son_filters={"is_student": True}
)

方案2:通用递归实现(适合层级扩展)

如果后续可能新增更多家族层级,可使用递归实现,适配任意深度的树结构:

def get_recursive_node(person, level_filters, current_level=0):
    # 层级配置映射,后续新增层级直接在此处添加即可
    level_config = [
        {"model": Grandpa, "child_related": "fathers"},
        {"model": Father, "child_related": "sons"},
        {"model": Son, "child_related": None}
    ]
    # 组装当前节点数据
    node_data = {
        "id": person.id,
        "name": person.name,
        "level": level_config[current_level]["model"].__name__,
        "children": []
    }
    # 存在下一层级则递归查询
    child_related = level_config[current_level]["child_related"]
    if child_related:
        next_level = current_level + 1
        next_filter = level_filters.get(next_level, {})
        children = getattr(person, child_related).filter(**next_filter)
        for child in children:
            node_data["children"].append(get_recursive_node(child, level_filters, next_level))
    return node_data

def get_general_family_tree(level_filters):
    top_filter = level_filters.get(0, {})
    top_nodes = Grandpa.objects.filter(**top_filter)
    return [get_recursive_node(node, level_filters) for node in top_nodes]

调用示例

# 键为层级序号,0对应Grandpa、1对应Father、2对应Son
level_filters = {
    0: {"age__gt": 70},
    1: {"age__range": (40, 50)},
    2: {"is_student": True}
}
tree = get_general_family_tree(level_filters)

提示:两种方案均支持不传对应层级的过滤参数,默认返回该层级全量符合关联关系的节点。

内容的提问来源于stack exchange,提问作者Poula Adel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 06:36:08