CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-treebeard

Efficient tree implementations for Django models providing three different tree algorithms with unified API

Pending
Overview
Eval results
Files

tree-operations.mddocs/

Tree Operations

Core methods for creating, reading, updating, and deleting tree nodes. These operations work consistently across all tree implementations (AL_Node, MP_Node, NS_Node) while being optimized for each algorithm's characteristics.

Capabilities

Node Creation

Methods for adding new nodes to the tree structure.

Adding Root Nodes

@classmethod
def add_root(**kwargs):
    """
    Add a root node to the tree.
    
    The new root node becomes the rightmost root node.
    
    Parameters:
        **kwargs: Field values for the new node
        
    Returns:
        Node: The created and saved node instance
        
    Raises:
        NodeAlreadySaved: If 'instance' parameter is already saved
    """

Usage example:

# Create root nodes
electronics = Category.add_root(name='Electronics', slug='electronics')
books = Category.add_root(name='Books', slug='books')

Adding Child Nodes

def add_child(**kwargs):
    """
    Add a child node to this node.
    
    Child is added as the rightmost child.
    
    Parameters:
        **kwargs: Field values for the new child node
        
    Returns:
        Node: The created and saved child node
        
    Raises:
        NodeAlreadySaved: If 'instance' parameter is already saved
    """

Usage example:

# Add children to existing nodes
phones = electronics.add_child(name='Phones', slug='phones')
laptops = electronics.add_child(name='Laptops', slug='laptops')

Adding Sibling Nodes

def add_sibling(pos=None, **kwargs):
    """
    Add a sibling node to this node.
    
    Parameters:
        pos (str, optional): Position relative to current node
            - 'first-sibling': Leftmost sibling position
            - 'left': Before current node
            - 'right': After current node  
            - 'last-sibling': Rightmost sibling position
            - 'sorted-sibling': Position based on node_order_by
        **kwargs: Field values for the new sibling node
        
    Returns:
        Node: The created and saved sibling node
        
    Raises:
        InvalidPosition: If pos value is invalid
        NodeAlreadySaved: If 'instance' parameter is already saved
    """

Usage example:

# Add sibling nodes with positioning
tablets = phones.add_sibling('right', name='Tablets', slug='tablets')
accessories = laptops.add_sibling('left', name='Accessories', slug='accessories')

Bulk Operations

Methods for loading and exporting tree data structures.

Loading Bulk Data

@classmethod
def load_bulk(bulk_data, parent=None, keep_ids=False):
    """
    Load a tree structure from nested data.
    
    Efficiently creates multiple nodes from hierarchical data structure.
    
    Parameters:
        bulk_data (list): List of dictionaries representing nodes
            Each dict can contain 'children' key with nested nodes
        parent (Node, optional): Parent node for the loaded data
        keep_ids (bool): Whether to preserve 'id' values from data
        
    Returns:
        list: List of created root nodes
        
    Example data format:
        [
            {'name': 'Root1', 'children': [
                {'name': 'Child1'},
                {'name': 'Child2', 'children': [
                    {'name': 'Grandchild1'}
                ]}
            ]},
            {'name': 'Root2'}
        ]
    """

Usage example:

data = [
    {
        'name': 'Science',
        'children': [
            {'name': 'Physics'},
            {'name': 'Chemistry', 'children': [
                {'name': 'Organic'},
                {'name': 'Inorganic'}
            ]}
        ]
    },
    {'name': 'Fiction'}
]
Category.load_bulk(data, parent=books)

Exporting Bulk Data

@classmethod
def dump_bulk(parent=None, keep_ids=True):
    """
    Export tree structure to nested data format.
    
    Parameters:
        parent (Node, optional): Root node to export from (default: all roots)
        keep_ids (bool): Whether to include node IDs in export
        
    Returns:
        list: Nested list/dict structure representing the tree
    """

Usage example:

# Export entire tree
tree_data = Category.dump_bulk()

# Export specific subtree  
subtree_data = Category.dump_bulk(parent=electronics, keep_ids=False)

Node Movement

Methods for moving nodes within the tree structure.

def move(target, pos=None):
    """
    Move this node to a new position in the tree.
    
    Parameters:
        target (Node): Reference node for the move operation
        pos (str, optional): Position relative to target node
            - 'first-child': Leftmost child of target
            - 'last-child': Rightmost child of target
            - 'sorted-child': Child position based on node_order_by
            - 'first-sibling': Leftmost sibling of target
            - 'left': Before target node
            - 'right': After target node
            - 'last-sibling': Rightmost sibling of target
            - 'sorted-sibling': Sibling position based on node_order_by
            
    Raises:
        InvalidPosition: If pos value is invalid
        InvalidMoveToDescendant: If trying to move to descendant
    """

Usage example:

# Move phone to be child of accessories
phones.move(accessories, 'last-child')

# Move node to specific sibling position
tablets.move(laptops, 'left')

# Move with automatic positioning (requires node_order_by)
smartphones.move(electronics, 'sorted-child')

Node Deletion

Methods for removing nodes and their descendants.

def delete(self):
    """
    Delete this node and all its descendants.
    
    Automatically handles tree structure updates and descendant removal.
    For large subtrees, this operation may be expensive.
    
    Returns:
        tuple: (number_deleted, {model_label: count})
    """

Usage example:

# Delete node and all descendants
phones.delete()  # Also deletes smartphones, tablets, etc.

# Delete multiple nodes efficiently (using queryset)
Category.objects.filter(name__startswith='Old').delete()

Root Node Management

Methods for working with root-level nodes.

@classmethod
def get_root_nodes(cls):
    """
    Get queryset of all root nodes.
    
    Returns:
        QuerySet: All root nodes in tree order
    """

@classmethod  
def get_first_root_node(cls):
    """
    Get the first root node.
    
    Returns:
        Node or None: First root node or None if no roots exist
    """

@classmethod
def get_last_root_node(cls):
    """
    Get the last root node.
    
    Returns:
        Node or None: Last root node or None if no roots exist  
    """

Usage example:

# Get all root categories
roots = Category.get_root_nodes()

# Get specific root nodes
first_root = Category.get_first_root_node()
last_root = Category.get_last_root_node()

# Iterate through roots
for root in Category.get_root_nodes():
    print(f"Root: {root.name}")

Position Constants

Valid Positions for add_sibling()

_valid_pos_for_add_sibling = [
    'first-sibling',  # New leftmost sibling
    'left',           # Before current node
    'right',          # After current node
    'last-sibling',   # New rightmost sibling
    'sorted-sibling'  # Position by node_order_by
]

Valid Positions for move()

_valid_pos_for_move = [
    'first-child',    # New leftmost child
    'last-child',     # New rightmost child
    'sorted-child',   # Child position by node_order_by
    'first-sibling',  # New leftmost sibling
    'left',           # Before target node
    'right',          # After target node
    'last-sibling',   # New rightmost sibling
    'sorted-sibling'  # Sibling position by node_order_by
]

Advanced Usage Patterns

Ordered Tree Operations

When node_order_by is configured, use sorted positions for automatic ordering:

class Category(MP_Node):
    name = models.CharField(max_length=100)
    priority = models.IntegerField(default=0)
    
    node_order_by = ['priority', 'name']

# Nodes will be automatically positioned by priority, then name
cat1 = Category.add_root(name='High Priority', priority=1)
cat2 = Category.add_root(name='Low Priority', priority=10)  # Auto-positioned after cat1

child = cat1.add_child(name='Child', priority=5)
child.move(cat2, 'sorted-child')  # Positioned by priority within cat2's children

Bulk Loading with Foreign Keys

Handle foreign key relationships during bulk loading:

# Data with foreign key references
data = [
    {
        'name': 'Books',
        'category_type_id': 1,  # References CategoryType model
        'children': [
            {'name': 'Fiction', 'category_type_id': 2},
            {'name': 'Non-Fiction', 'category_type_id': 2}
        ]
    }
]

# Foreign keys are automatically handled
Category.load_bulk(data)

Transaction Safety

Large tree operations should use database transactions:

from django.db import transaction

with transaction.atomic():
    # Multiple tree operations as single transaction
    root = Category.add_root(name='New Section')
    
    for item in bulk_items:
        root.add_child(**item)
    
    # Move existing subtree
    old_section.move(root, 'first-child')

Performance Considerations

  • Bulk Loading: Use load_bulk() instead of multiple add_child() calls
  • Large Moves: Moving large subtrees can be expensive, especially in NS_Node
  • Deletions: Deleting nodes with many descendants requires multiple database operations
  • Transactions: Wrap multiple operations in transactions for consistency and performance

Error Handling

from treebeard.exceptions import InvalidPosition, InvalidMoveToDescendant

try:
    # Attempt to move node
    child.move(grandchild, 'first-child')  # Invalid: moving to descendant
except InvalidMoveToDescendant:
    print("Cannot move node to its own descendant")

try:
    # Invalid position
    node.add_sibling('invalid-position', name='Test')
except InvalidPosition:
    print("Invalid position specified")

Install with Tessl CLI

npx tessl i tessl/pypi-django-treebeard

docs

admin-integration.md

forms-ui.md

index.md

tree-models.md

tree-navigation.md

tree-operations.md

utilities.md

tile.json