or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-integration.mdforms-ui.mdindex.mdtree-models.mdtree-navigation.mdtree-operations.mdutilities.md

tree-operations.mddocs/

0

# Tree Operations

1

2

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.

3

4

## Capabilities

5

6

### Node Creation

7

8

Methods for adding new nodes to the tree structure.

9

10

#### Adding Root Nodes

11

12

```python { .api }

13

@classmethod

14

def add_root(**kwargs):

15

"""

16

Add a root node to the tree.

17

18

The new root node becomes the rightmost root node.

19

20

Parameters:

21

**kwargs: Field values for the new node

22

23

Returns:

24

Node: The created and saved node instance

25

26

Raises:

27

NodeAlreadySaved: If 'instance' parameter is already saved

28

"""

29

```

30

31

Usage example:

32

```python

33

# Create root nodes

34

electronics = Category.add_root(name='Electronics', slug='electronics')

35

books = Category.add_root(name='Books', slug='books')

36

```

37

38

#### Adding Child Nodes

39

40

```python { .api }

41

def add_child(**kwargs):

42

"""

43

Add a child node to this node.

44

45

Child is added as the rightmost child.

46

47

Parameters:

48

**kwargs: Field values for the new child node

49

50

Returns:

51

Node: The created and saved child node

52

53

Raises:

54

NodeAlreadySaved: If 'instance' parameter is already saved

55

"""

56

```

57

58

Usage example:

59

```python

60

# Add children to existing nodes

61

phones = electronics.add_child(name='Phones', slug='phones')

62

laptops = electronics.add_child(name='Laptops', slug='laptops')

63

```

64

65

#### Adding Sibling Nodes

66

67

```python { .api }

68

def add_sibling(pos=None, **kwargs):

69

"""

70

Add a sibling node to this node.

71

72

Parameters:

73

pos (str, optional): Position relative to current node

74

- 'first-sibling': Leftmost sibling position

75

- 'left': Before current node

76

- 'right': After current node

77

- 'last-sibling': Rightmost sibling position

78

- 'sorted-sibling': Position based on node_order_by

79

**kwargs: Field values for the new sibling node

80

81

Returns:

82

Node: The created and saved sibling node

83

84

Raises:

85

InvalidPosition: If pos value is invalid

86

NodeAlreadySaved: If 'instance' parameter is already saved

87

"""

88

```

89

90

Usage example:

91

```python

92

# Add sibling nodes with positioning

93

tablets = phones.add_sibling('right', name='Tablets', slug='tablets')

94

accessories = laptops.add_sibling('left', name='Accessories', slug='accessories')

95

```

96

97

### Bulk Operations

98

99

Methods for loading and exporting tree data structures.

100

101

#### Loading Bulk Data

102

103

```python { .api }

104

@classmethod

105

def load_bulk(bulk_data, parent=None, keep_ids=False):

106

"""

107

Load a tree structure from nested data.

108

109

Efficiently creates multiple nodes from hierarchical data structure.

110

111

Parameters:

112

bulk_data (list): List of dictionaries representing nodes

113

Each dict can contain 'children' key with nested nodes

114

parent (Node, optional): Parent node for the loaded data

115

keep_ids (bool): Whether to preserve 'id' values from data

116

117

Returns:

118

list: List of created root nodes

119

120

Example data format:

121

[

122

{'name': 'Root1', 'children': [

123

{'name': 'Child1'},

124

{'name': 'Child2', 'children': [

125

{'name': 'Grandchild1'}

126

]}

127

]},

128

{'name': 'Root2'}

129

]

130

"""

131

```

132

133

Usage example:

134

```python

135

data = [

136

{

137

'name': 'Science',

138

'children': [

139

{'name': 'Physics'},

140

{'name': 'Chemistry', 'children': [

141

{'name': 'Organic'},

142

{'name': 'Inorganic'}

143

]}

144

]

145

},

146

{'name': 'Fiction'}

147

]

148

Category.load_bulk(data, parent=books)

149

```

150

151

#### Exporting Bulk Data

152

153

```python { .api }

154

@classmethod

155

def dump_bulk(parent=None, keep_ids=True):

156

"""

157

Export tree structure to nested data format.

158

159

Parameters:

160

parent (Node, optional): Root node to export from (default: all roots)

161

keep_ids (bool): Whether to include node IDs in export

162

163

Returns:

164

list: Nested list/dict structure representing the tree

165

"""

166

```

167

168

Usage example:

169

```python

170

# Export entire tree

171

tree_data = Category.dump_bulk()

172

173

# Export specific subtree

174

subtree_data = Category.dump_bulk(parent=electronics, keep_ids=False)

175

```

176

177

### Node Movement

178

179

Methods for moving nodes within the tree structure.

180

181

```python { .api }

182

def move(target, pos=None):

183

"""

184

Move this node to a new position in the tree.

185

186

Parameters:

187

target (Node): Reference node for the move operation

188

pos (str, optional): Position relative to target node

189

- 'first-child': Leftmost child of target

190

- 'last-child': Rightmost child of target

191

- 'sorted-child': Child position based on node_order_by

192

- 'first-sibling': Leftmost sibling of target

193

- 'left': Before target node

194

- 'right': After target node

195

- 'last-sibling': Rightmost sibling of target

196

- 'sorted-sibling': Sibling position based on node_order_by

197

198

Raises:

199

InvalidPosition: If pos value is invalid

200

InvalidMoveToDescendant: If trying to move to descendant

201

"""

202

```

203

204

Usage example:

205

```python

206

# Move phone to be child of accessories

207

phones.move(accessories, 'last-child')

208

209

# Move node to specific sibling position

210

tablets.move(laptops, 'left')

211

212

# Move with automatic positioning (requires node_order_by)

213

smartphones.move(electronics, 'sorted-child')

214

```

215

216

### Node Deletion

217

218

Methods for removing nodes and their descendants.

219

220

```python { .api }

221

def delete(self):

222

"""

223

Delete this node and all its descendants.

224

225

Automatically handles tree structure updates and descendant removal.

226

For large subtrees, this operation may be expensive.

227

228

Returns:

229

tuple: (number_deleted, {model_label: count})

230

"""

231

```

232

233

Usage example:

234

```python

235

# Delete node and all descendants

236

phones.delete() # Also deletes smartphones, tablets, etc.

237

238

# Delete multiple nodes efficiently (using queryset)

239

Category.objects.filter(name__startswith='Old').delete()

240

```

241

242

### Root Node Management

243

244

Methods for working with root-level nodes.

245

246

```python { .api }

247

@classmethod

248

def get_root_nodes(cls):

249

"""

250

Get queryset of all root nodes.

251

252

Returns:

253

QuerySet: All root nodes in tree order

254

"""

255

256

@classmethod

257

def get_first_root_node(cls):

258

"""

259

Get the first root node.

260

261

Returns:

262

Node or None: First root node or None if no roots exist

263

"""

264

265

@classmethod

266

def get_last_root_node(cls):

267

"""

268

Get the last root node.

269

270

Returns:

271

Node or None: Last root node or None if no roots exist

272

"""

273

```

274

275

Usage example:

276

```python

277

# Get all root categories

278

roots = Category.get_root_nodes()

279

280

# Get specific root nodes

281

first_root = Category.get_first_root_node()

282

last_root = Category.get_last_root_node()

283

284

# Iterate through roots

285

for root in Category.get_root_nodes():

286

print(f"Root: {root.name}")

287

```

288

289

## Position Constants

290

291

### Valid Positions for add_sibling()

292

293

```python { .api }

294

_valid_pos_for_add_sibling = [

295

'first-sibling', # New leftmost sibling

296

'left', # Before current node

297

'right', # After current node

298

'last-sibling', # New rightmost sibling

299

'sorted-sibling' # Position by node_order_by

300

]

301

```

302

303

### Valid Positions for move()

304

305

```python { .api }

306

_valid_pos_for_move = [

307

'first-child', # New leftmost child

308

'last-child', # New rightmost child

309

'sorted-child', # Child position by node_order_by

310

'first-sibling', # New leftmost sibling

311

'left', # Before target node

312

'right', # After target node

313

'last-sibling', # New rightmost sibling

314

'sorted-sibling' # Sibling position by node_order_by

315

]

316

```

317

318

## Advanced Usage Patterns

319

320

### Ordered Tree Operations

321

322

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

323

324

```python

325

class Category(MP_Node):

326

name = models.CharField(max_length=100)

327

priority = models.IntegerField(default=0)

328

329

node_order_by = ['priority', 'name']

330

331

# Nodes will be automatically positioned by priority, then name

332

cat1 = Category.add_root(name='High Priority', priority=1)

333

cat2 = Category.add_root(name='Low Priority', priority=10) # Auto-positioned after cat1

334

335

child = cat1.add_child(name='Child', priority=5)

336

child.move(cat2, 'sorted-child') # Positioned by priority within cat2's children

337

```

338

339

### Bulk Loading with Foreign Keys

340

341

Handle foreign key relationships during bulk loading:

342

343

```python

344

# Data with foreign key references

345

data = [

346

{

347

'name': 'Books',

348

'category_type_id': 1, # References CategoryType model

349

'children': [

350

{'name': 'Fiction', 'category_type_id': 2},

351

{'name': 'Non-Fiction', 'category_type_id': 2}

352

]

353

}

354

]

355

356

# Foreign keys are automatically handled

357

Category.load_bulk(data)

358

```

359

360

### Transaction Safety

361

362

Large tree operations should use database transactions:

363

364

```python

365

from django.db import transaction

366

367

with transaction.atomic():

368

# Multiple tree operations as single transaction

369

root = Category.add_root(name='New Section')

370

371

for item in bulk_items:

372

root.add_child(**item)

373

374

# Move existing subtree

375

old_section.move(root, 'first-child')

376

```

377

378

### Performance Considerations

379

380

- **Bulk Loading**: Use `load_bulk()` instead of multiple `add_child()` calls

381

- **Large Moves**: Moving large subtrees can be expensive, especially in NS_Node

382

- **Deletions**: Deleting nodes with many descendants requires multiple database operations

383

- **Transactions**: Wrap multiple operations in transactions for consistency and performance

384

385

### Error Handling

386

387

```python

388

from treebeard.exceptions import InvalidPosition, InvalidMoveToDescendant

389

390

try:

391

# Attempt to move node

392

child.move(grandchild, 'first-child') # Invalid: moving to descendant

393

except InvalidMoveToDescendant:

394

print("Cannot move node to its own descendant")

395

396

try:

397

# Invalid position

398

node.add_sibling('invalid-position', name='Test')

399

except InvalidPosition:

400

print("Invalid position specified")

401

```