Skip to main content
https://bitbucket-badges.atlassian.io/badge/saaj/fangorn.svg?ref=default https://codecov.io/bitbucket/saaj/fangorn/branch/default/graph/badge.svg https://badge.fury.io/py/Fangorn.png

Fangorn

Nested Sets aka Modified Pre-order Tree Traversal (MPTT) SQL tree implemented in Python for MySQL and SQLite. Uses both traversal markup (left, right) and adjacency list parentId for more ad-hoc query flexibility.

Provides tree structure validation and “memorisation” via SQLite :memory: for quick reads.

Example

We want to achieve the following tree. Node is represented by name id → parentId (l, r). To output a tree this way fangorn.test.visualize function can be used.

R 1 → None (1, 18)
└─A1 2 → 1 (2, 5)
  └─B1 3 → 2 (3, 4)
└─A2 4 → 1 (6, 13)
  └─B2 5 → 4 (7, 8)
  └─B3 6 → 4 (9, 12)
    └─C1 7 → 6 (10, 11)
└─A3 8 → 1 (14, 17)
  └─B4 9 → 8 (15, 16)

First we need a table to represent the tree. And we want a tree node to have a name.

import MySQLdb as mysql
conn = mysql.connect(user = 'guest', db = 'test')
conn.query('''
  CREATE TABLE `node` (
    `node_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `parent_id` int(10) unsigned DEFAULT NULL,
    `l` int(10) unsigned NOT NULL,
    `r` int(10) unsigned NOT NULL,
    `name` varchar(8) NOT NULL,
    PRIMARY KEY (`node_id`),
    KEY `l` (`l`),
    KEY `r` (`r`),
    KEY `parent_id` (`parent_id`),
    CONSTRAINT `node_has_node` FOREIGN KEY (`parent_id`)
      REFERENCES `node` (`node_id`)
      ON DELETE CASCADE
      ON UPDATE CASCADE
  ) ENGINE=InnoDB;
''')

Now we can create tree instance. Note that DAO that the tree relies on is expected to support named DB-API paramstyle (i.e. WHERE node_id = :nodeId). Also transaction control methods are recommended to implement nested transaction support. However test suite requires nested transactions to run. For MySQLdb and sqlite3 there’re compatibility wrappers under fangorn.compat.

import fangorn
from fangorn.compat.mysqldb import Mysqldb as MysqldbWrapper
tree = fangorn.NestedSetsTree(MysqldbWrapper(conn), 'node', ('name',))

rId  = tree.add(dict(name = 'R'))
a1Id = tree.add(dict(name = 'A1'), parentId = rId)
tree.add(dict(name = 'B1'), parentId = a1Id)
a2Id = tree.add(dict(name = 'A2'), parentId = rId)
b2Id = tree.add(dict(name = 'B2'), parentId = a2Id)
b3Id = tree.add(dict(name = 'B3'), prevId = b2Id)
tree.add(dict(name = 'C1'), parentId = b3Id)
a3Id = tree.add(dict(name = 'A3'), parentId = rId)
tree.add(dict(name = 'B4'), parentId = a3Id)

tree.move(a1Id, rId)
tree.move(a3Id, prevId = a2Id)

Now we can play with the tree.

print(tree.isDescendantOf(a2Id, 4)) # False
print(tree.isDescendantOf(a2Id, 6)) # True
print(tree.isDescendantOf(a2Id, 7)) # True
print(tree.isDescendantOf(a2Id, 9)) # False

print([n['name'] for n in tree.getChildren(a2Id)])    # ['B2', 'B3']
print([n['name'] for n in tree.getDescendants(a2Id)]) # ['B2', 'B3', 'C1']
print([n['name'] for n in tree.getPath(7)])           # ['R', 'A2', 'B3', 'C1']

print(tree.getNode(8))   # {'left': 14L, 'right': 17L, 'nodeId': 8L, 'name': 'A3', 'parentId': 1L}
print(tree.getParent(8)) # {'left': 1L, 'right': 18L, 'nodeId': 1L, 'name': 'R', 'parentId': None}
print(tree.getRoot())    # {'left': 1L, 'right': 18L, 'nodeId': 1L, 'name': 'R', 'parentId': None}

tree.edit(1, dict(name = 'RR'))
print(tree.getRoot()) # {'left': 1L, 'right': 18L, 'nodeId': 1L, 'name': 'RR', 'parentId': None}

print([n['name'] for n in tree.getDescendants(a2Id)] # ['B2', 'B3', 'C1'])
tree.remove(b3Id)
print([n['name'] for n in tree.getDescendants(a2Id)] # ['B2'])

For more usage examples look at project’s test suite.

Metadata

Release files for Fangorn 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for Fangorn 0.3.2
File Size Uploaded
Fangorn-0.3.2.tar.gz 52.6 kB Details

Release files / Fangorn-0.3.2.tar.gz

Download URL Fangorn-0.3.2.tar.gz
Size 52.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1fe6f17b16ed36a42885ce39f0a9aa2fe2ff14f3cb91960af6843b842e536e9e
BLAKE2b-256 checksum
How to use checksums
c2cd94b08418e03542cc96d706dfdb2b5307f54a200ca56c3cf975f4555cef04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Release history Release notifications | RSS feed

This release

0.3.2 This release

1 release file

0.3.1

1 release file

0.3.0

1 release file

0.2.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page