15

我试图获取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 来代替运行吗?在这里你可以假设我有一个 Node 的 pk 列表。

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

4

3 回答 3

11

更高版本的 mptt 已经在对象管理器中内置了这个功能。所以解决这个问题的方法如下:

Node.objects.get_queryset_descendants(my_queryset, include_self=False)
于 2016-01-13T18:52:36.970 回答
10

非常感谢 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>] 
于 2011-04-21T07:07:31.927 回答
1

Django mptt 使用MySQL 管理分层数据文档中描述的修改前序树遍历方法。

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

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 结合起来,您将获得模型。

于 2011-04-20T09:10:59.187 回答