Efficient tree implementations for Django models providing three different tree algorithms with unified API
npx @tessl/cli install tessl/pypi-django-treebeard@4.7.0Django Treebeard provides efficient tree data structure implementations for Django models. It offers three different tree algorithms (Adjacency List, Materialized Path, and Nested Sets) with a unified API, enabling developers to store and manipulate hierarchical data with optimized database operations.
pip install django-treebeardfrom treebeard.al_tree import AL_Node
from treebeard.mp_tree import MP_Node
from treebeard.ns_tree import NS_NodeFor admin integration:
from treebeard.admin import TreeAdminFor forms:
from treebeard.forms import MoveNodeForm, movenodeform_factoryFor exceptions:
from treebeard.exceptions import (
InvalidPosition, InvalidMoveToDescendant,
NodeAlreadySaved, MissingNodeOrderBy, PathOverflow
)from django.db import models
from treebeard.mp_tree import MP_Node
class Category(MP_Node):
name = models.CharField(max_length=50)
node_order_by = ['name']
# Create root nodes
root1 = Category.add_root(name='Electronics')
root2 = Category.add_root(name='Books')
# Add children
phones = root1.add_child(name='Phones')
laptops = root1.add_child(name='Laptops')
# Add grandchildren
smartphones = phones.add_child(name='Smartphones')
tablets = phones.add_child(name='Tablets')
# Navigate the tree
print(root1.get_children()) # [phones, laptops]
print(phones.get_parent()) # root1
print(root1.get_descendants()) # [phones, laptops, smartphones, tablets]
print(smartphones.get_ancestors()) # [root1, phones]
# Move nodes
smartphones.move(laptops, pos='first-child')
# Bulk operations
data = [
{'name': 'Science'},
{'name': 'Fiction', 'children': [
{'name': 'Sci-Fi'},
{'name': 'Fantasy'}
]}
]
Category.load_bulk(data, parent=root2)Django Treebeard provides three tree implementations, each optimized for different use cases:
All implementations inherit from the abstract Node class, providing consistent methods for tree operations regardless of the underlying algorithm.
Base classes for creating tree models using different algorithms, each with specific performance characteristics and database storage patterns.
class AL_Node(Node):
"""Adjacency List implementation"""
# Requires: parent (ForeignKey), sib_order (PositiveIntegerField)
class MP_Node(Node):
"""Materialized Path implementation"""
path = models.CharField(max_length=255, unique=True)
depth = models.PositiveIntegerField()
numchild = models.PositiveIntegerField()
class NS_Node(Node):
"""Nested Sets implementation"""
lft = models.PositiveIntegerField(db_index=True)
rgt = models.PositiveIntegerField(db_index=True)
tree_id = models.PositiveIntegerField(db_index=True)
depth = models.PositiveIntegerField(db_index=True)Core methods for creating, reading, updating, and deleting tree nodes, including bulk operations and tree structure manipulation.
# Class methods
@classmethod
def add_root(**kwargs): ...
@classmethod
def load_bulk(bulk_data, parent=None, keep_ids=False): ...
@classmethod
def get_root_nodes(): ...
# Instance methods
def add_child(**kwargs): ...
def add_sibling(pos=None, **kwargs): ...
def move(target, pos=None): ...
def delete(): ...Methods for traversing tree structures, getting related nodes, and querying tree relationships.
def get_parent(update=False): ...
def get_children(): ...
def get_siblings(): ...
def get_ancestors(): ...
def get_descendants(): ...
def get_root(): ...
def is_child_of(node): ...
def is_descendant_of(node): ...Admin classes and utilities for managing tree structures through Django's admin interface with drag-and-drop functionality.
class TreeAdmin(admin.ModelAdmin):
def changelist_view(request, extra_context=None): ...
def move_node(request): ...
def admin_factory(form_class): ...Form classes for tree manipulation in Django applications, including move operations and tree rendering.
class MoveNodeForm(forms.ModelForm):
def save(commit=True): ...
def mk_dropdown_tree(model, for_node=None): ...
def movenodeform_factory(model, form=MoveNodeForm, **kwargs): ...Helper functions for number base conversion, tree validation, and database-specific operations.
# Number conversion for MP trees
def int2str(num, radix=10, alphabet=BASE85): ...
def str2int(num, radix=10, alphabet=BASE85): ...
# Tree validation
@classmethod
def find_problems(): ...
@classmethod
def fix_tree(): ...Django Treebeard defines specific exceptions for tree operations:
class InvalidPosition(Exception):
"""Raised when passing an invalid pos value"""
class InvalidMoveToDescendant(Exception):
"""Raised when attempting to move a node to one of its descendants"""
class NodeAlreadySaved(Exception):
"""Raised when attempting to add a node which is already saved"""
class MissingNodeOrderBy(Exception):
"""Raised when node_order_by attribute is missing but required"""
class PathOverflow(Exception):
"""Raised when no more space available in MP tree path"""Set node_order_by on your model class to enable automatic node ordering:
class Category(MP_Node):
name = models.CharField(max_length=50)
created_date = models.DateTimeField(auto_now_add=True)
node_order_by = ['name', 'created_date']Materialized Path nodes support additional configuration:
class Category(MP_Node):
name = models.CharField(max_length=50)
steplen = 4 # Path step length
alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' # Path encoding alphabet
gap = 10 # Gap between path values for insertionsChoose the implementation based on your specific use case and performance requirements.