Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django-mptt get_descendants for a list of nodes

I am trying to get all descendants(include_self=True) not for one Node, but for a list (a QuerySet) of Nodes. This should be one SQL query.

Example (that actually is not working:)

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" 

The only idea I have right now is to iterate through some_nodes and run get_descendants() for every node - but this is terrible solution (plenty of SQL queries).

If there is no clean way to do it via Django ORM can you provide me a custom SQL to run instead? Here you can make assumption that I have a list of Node's pk.

EDIT: If that could help - all of my "some_nodes" are placed in the same parent directory and have the same "level" in the tree.

like image 863
thedk Avatar asked Apr 19 '11 21:04

thedk


People also ask

How do you name descendants of root nodes in a tree?

All descendants of root nodes will be given the same tree id as their root node. The name of a field which contains the (zero-based) level at which an item sits in the tree, which should be a PositiveIntegerField . Defaults to 'level'. For example, root nodes would have a level of 0 and their immediate children would have have a level of 1.

Why are there dangling references in my Django-mptt tree?

In cases of insertion or other updates, where django-mptt will need to re-order the tree you will most likely be left with dangling references. Please see order_insertion_by gotcha for details.

Can nodes in a tree have multiple parents?

A tree where nodes can have multiple parents isn’t really a tree at all. The default manager for an MPTTModel is a TreeManager. Any QuerySet created with this manager will be ordered based on the tree structure, with root nodes appearing in tree id order and their descendants being ordered in a depth-first fashion.

How do I register a model in Django-mptt?

In cases of insertion or other updates, where django-mptt will need to re-order the tree you will most likely be left with dangling references. Please see order_insertion_by gotcha for details. The preferred way to do model registration in django-mptt is by subclassing MPTTModel.


2 Answers

Later versions of mptt already have this function built into the object manager. So the solution to this is just as follows:

Node.objects.get_queryset_descendants(my_queryset, include_self=False)
like image 132
Cory Avatar answered Sep 21 '22 15:09

Cory


Great thanks to Craig de Stigter answered my question on django-mptt-dev group, in case anybody need it I am kindly reposting his solution from 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) 

Example Node tree:

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

Example usage:

   >> 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>] 
like image 25
thedk Avatar answered Sep 20 '22 15:09

thedk