【问题标题】:django-mptt get_descendants for a list of nodesdjango-mptt get_descendants 获取节点列表
【发布时间】:2011-08-09 00:42:00
【问题描述】:

我试图让所有descendants(include_self=True) 不为一个 节点,但用于节点列表(查询集)。这应该是一个 SQL 询问。

示例(实际上不起作用:)

some_nodes = Node.objects.filter( ...some_condition... ) 
some_nodes.get_descendants(include_self=True) #hopefully I would like 
to have all possible Nodes starting from every node of "some_nodes" 

我现在唯一的想法是遍历 some_nodes 和 为每个节点运行 get_descendants() - 但这是一个糟糕的解决方案 (大量的 SQL 查询)。

如果没有通过 Django ORM 实现的干净方法,您能否为我提供一个自定义的 改为运行 SQL 吗?在这里你可以假设我有一个列表 节点的pk。

编辑:如果有帮助的话 - 我所有的“some_nodes”都放在同一个父目录中,并且在树中具有相同的“级别”。

【问题讨论】:

    标签: python django list mptt descendant


    【解决方案1】:

    Django mptt 使用MySQL Managing Hierarchical Data 文档中描述的修改前序树遍历方法。

    它具有以下查询,用于返回某个节点下的树中的所有节点:

    SELECT node.name
    FROM nested_category AS node, nested_category AS parent
    WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND parent.name = 'ELECTRONICS'
    ORDER BY node.lft;
    

    秘密是parent.lft和parent.rgt的数字,所有的孩子都会有一个介于两者之间的node.lft值。

    显然,该示例仅假定只有一个父级,并且您需要使用父级名称来查找父级。由于您已经拥有父节点数据,因此您可以执行以下操作:

    SELECT node.id
    FROM node_table
    WHERE node.lft BETWEEN parent[0].lft AND parent[0].rgt
        OR node.lft BETWEEN parent[1].lft AND parent[1].rgt
    

    我将把它作为练习留给您,以了解如何为每个父节点生成单独的 BETWEEN 子句(提示,“AND”.join)

    或者,您可以在每个父级上使用范围生成器来获取每个父级的 lft 和 rgt 值(包括)之间的所有值。然后,您可以使用巨大的 IN 语句而不是大量的 BETWEEN 子句。

    将上述任何一个与 RawQueryset 结合起来,您将获得模型。

    【讨论】:

    • 嗯,我刚刚意识到我可能会自己使用它来解决为大量树提取父母的相关问题,由于大量的查询调用,目前这也非常慢生成
    • 这不是我问的非常准确的答案:此声明中没有 tree_id!因此,在 lft 和 right 之间选择节点的想法是正确的。
    【解决方案2】:

    非常感谢 Craig de Stigter 在 django-mptt-dev 小组上回答了我的问题,如果有人需要,我很乐意从 http://groups.google.com/group/django-mptt-dev/browse_thread/thread/637c8b2fe816304d

    转发 他的 解决方案
       from django.db.models import Q 
       import operator 
       def get_queryset_descendants(nodes, include_self=False): 
           if not nodes: 
               return Node.tree.none() 
           filters = [] 
           for n in nodes: 
               lft, rght = n.lft, n.rght 
               if include_self: 
                   lft -=1 
                   rght += 1 
               filters.append(Q(tree_id=n.tree_id, lft__gt=lft, rght__lt=rght)) 
           q = reduce(operator.or_, filters) 
           return Node.tree.filter(q) 
    

    示例节点树:

    T1 
    ---T1.1 
    ---T1.2 
    T2 
    T3 
    ---T3.3 
    ------T3.3.3 
    

    示例用法:

       >> some_nodes = [<Node: T1>, <Node: T2>, <Node: T3>]  # QureySet
       >> print get_queryset_descendants(some_nodes)
       [<Node: T1.1>, <Node: T1.2>, <Node: T3.3>, <Node: T3.3.3>] 
       >> print get_queryset_descendants(some_nodes, include_self=True)
       [<Node: T1>, <Node: T1.1>, <Node: T1.2>, <Node: T2>, <Node: T3>, <Node: T3.3>, <Node: T3.3.3>] 
    

    【讨论】:

    • 这太棒了!我认为它应该 Node.objects 而不是 Node.tree 虽然
    • 实际上这现在已内置在管理器中。请参阅我的替代答案。
    【解决方案3】:

    mptt 的更高版本已经have this function built into the object manager。所以解决方法如下:

    Node.objects.get_queryset_descendants(my_queryset, include_self=False)
    

    【讨论】:

      猜你喜欢
      • 2012-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多