# django-treenode: a tree model for Django that keeps the whole tree in memory

> django-treenode gives a Django model a parent pointer and a tree of cached nodes, so ancestors, descendants, siblings and roots come back without a query, and the admin renders the tree three different ways. The cost is a cache-backed in-memory structure that the package keeps in sync itself.

**fabiocaccamo/django-treenode** — :deciduous_tree: probably the best abstract model/admin for your tree based stuff.

- Repository: https://github.com/fabiocaccamo/django-treenode
- Stars: 804 · Forks: 39
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/fabiocaccamo-django-treenode

## The promise is traversal with no queries

The feature list is short and the first item carries the whole argument. Fast, you get ancestors, children, descendants, parent, root, siblings and tree with no queries. That is not a micro-optimisation, it is a change in the shape of the code you write. A nested-set or adjacency-list model forces every traversal into a recursive descent of database round trips, so the cost of reading a subtree scales with its depth. django-treenode instead keeps a cached tree of nodes in memory and serves the traversal from that. The other three feature lines describe how that cache stays true. Synced, in-memory model instances are automatically updated. Compatibility, you can easily add treenode to existing projects. No dependencies, and the requirements file confirms it, listing only django >= 4.2. Easy configuration, you just extend the abstract model and model admin. The repository topics list the vocabulary it is built around: node, nodes, parent, children, ancestors, descendants, siblings, root, roots and tree.

## The model gains prefixed fields and a large method surface

The configuration section shows a model inheriting from treenode.models.TreeNodeModel, which is an abstract class:

```python
from django.db import models

from treenode.models import TreeNodeModel


class Category(TreeNodeModel):

    # the field used to display the model instance
    # default value 'pk'
    treenode_display_field = "name"

    name = models.CharField(max_length=50)

    class Meta(TreeNodeModel.Meta):
        verbose_name = "Category"
        verbose_name_plural = "Categories"
```

It adds many fields, prefixed with tn_ to prevent direct access, and a large set of public methods. The listing is long and specific. Ancestors come in four flavours: get_ancestors, get_ancestors_count, get_ancestors_pks and get_ancestors_queryset, and the same quartet exists for children, descendants and siblings, where each family gives you the objects themselves, a count, a list of primary keys and a queryset. Alongside those sit get_parent, get_parent_pk, set_parent, get_root, get_root_pk, get_roots, get_roots_queryset, get_depth, get_level, get_index, get_order, get_priority and set_priority, plus is_ancestor_of, get_first_child, get_last_child, get_tree, get_tree_display, get_descendants_tree and get_descendants_tree_display. The consistency is the point, because a uniform surface means the same call works whichever direction you are walking. The README also flags the one trap: if you extend a model that already has fields, make sure your existing field names do not clash with the public methods and properties.

## The admin can draw the tree three different ways

The admin integration is described as a great tree visualization with three display modes, and the repository contains a screenshot for each. Indentation is the default. Breadcrumbs is the mode that shows the path from the root down to each row. Accordion is the mode that collapses and expands subtrees. The admin class inherits from treenode.admin.TreeNodeModelAdmin and the mode is set with a class attribute, treenode_display_mode, using the constants TreeNodeModelAdmin.TREENODE_DISPLAY_MODE_ACCORDION, TREENODE_DISPLAY_MODE_BREADCRUMBS and TREENODE_DISPLAY_MODE_INDENTATION. There is a detail in the comment worth knowing: when changelist results are filtered by a querystring, breadcrumbs mode is used instead, to preserve data display integrity. That is a small thing that tells you the author has thought about what happens when the tree view is no longer a complete tree. The admin also accepts a custom form, TreeNodeForm, which automatically excludes invalid parent choices, so a user cannot create a cycle through the interface.

## A separate cache backend is the configuration knob

There is exactly one settings change, and it is about caching. You can use a custom cache backend by adding a treenode entry to settings.CACHES, otherwise the default cache backend is used. The example shows a file-based cache as the default and a LocMemCache for the treenode key, with the caveat that the treenode entry is the one that matters, since that is where the tree structure lives. This is the piece to think about before adopting the library, because the in-memory tree is only as fast as the cache you give it, and a LocMemCache is per process, which means a multi-process deployment has one tree per worker. The README does not discuss that consequence, so if you run several gunicorn workers or several containers, the behaviour of the cached tree under concurrent writes is worth testing rather than assuming. Everything else about the library is self-contained, which is why the default is to leave the cache alone and start with whatever your project already uses.

## Five steps from nothing to a working tree

The installation is five steps and there is nothing to configure beyond them. Run pip install django-treenode. Add treenode to settings.INSTALLED_APPS. Make your model inherit from treenode.models.TreeNodeModel. Make your model admin inherit from treenode.admin.TreeNodeModelAdmin. Then run python manage.py makemigrations and python manage.py migrate. The migration step is the one people rush, because the model gains the tn_ prefixed fields and the tree has to be populated with the existing rows, and skipping the migrate leaves you with a model that looks right and a tree that is empty. The model also carries a display field hint, treenode_display_field, which the abstract class uses to show an instance, defaulting to pk, and in the example it is set to name so the tree reads as labels rather than identifiers.

## Moving a node is set_parent, and the tree keeps itself consistent

The method list is worth reading as an API for a tree rather than as a set of getters. set_parent is the write operation, and it is what makes the synced claim meaningful: the package updates the cached tree so that in-memory instances reflect the change without a refresh. delete is there alongside it, and delete_tree is a separate method, so you can remove a node and everything under it in one call. get_index and get_order give you position within the parent, and get_priority with set_priority gives you an explicit ordering you control, which is the mechanism for reordering siblings that a plain tree cannot express. get_breadcrumbs is the traversal that feeds the breadcrumb admin view. get_tree and get_tree_display are the whole-subtree calls, and get_descendants_tree plus get_descendants_tree_display are the subtree with its structure intact rather than flattened.

## Where a tree model stops being the right shape

The trade-off is the cache, and it is worth being explicit about it. The library's speed comes from holding the tree in memory, which means the tree is a second copy of the data you already have in the database, and the package is responsible for keeping the two in step. That works, and it works well for the common case of a hierarchy that changes rarely and is read often, such as categories, a documentation tree or a site menu. It becomes awkward when writes are frequent or when the tree is large relative to memory, because the cost of keeping the cache coherent is paid on every write rather than only on reads. There is also the deployment question noted earlier, where a per-process cache means the tree is duplicated per worker and concurrent writes have to be reconciled. If your tree is small and mostly read, this package removes a lot of repetitive query code. If it is large and volatile, a path-column model or a nested set is a better fit.

## Conclusion

Adopt django-treenode if your Django project has a hierarchy, categories or a menu, and you want traversal methods that do not issue a query per level. Do not adopt it if you need trees that change on every request at scale, because the fast traversal depends on a cached in-memory tree the package maintains, and check that your existing field names do not clash with the library's public methods. Before you commit, read the installation steps in order, run the makemigrations and migrate pair, and confirm your Django version is among the supported classifiers.

## FAQ

### What is django-treenode?

It is an MIT licensed Django package described as probably the best abstract model and admin for tree based stuff. It adds an abstract model and an abstract model admin so you can model hierarchies such as categories, and the traversal methods return ancestors, descendants, siblings and roots with no queries.

### How do I install django-treenode?

Run pip install django-treenode, add treenode to settings.INSTALLED_APPS, make your model inherit from treenode.models.TreeNodeModel and your admin from treenode.admin.TreeNodeModelAdmin, then run python manage.py makemigrations and python manage.py migrate.

### Which Django and Python versions does django-treenode support?

The package classifiers list Django 4.2, 5.0, 5.1, 5.2, 6.0 and 6.1, and Python 3.11 through 3.14, with requires-python set to >=3.11. The only runtime requirement is django >= 4.2.

### How does django-treenode handle the cache?

The tree is kept in memory and served from the cache, which is why the package is fast. You can add a treenode entry to settings.CACHES to use a custom cache backend, otherwise the default cache backend is used, and the documented example pairs a FileBasedCache default with a LocMemCache for treenode.

## Sources

- [fabiocaccamo/django-treenode on GitHub](https://github.com/fabiocaccamo/django-treenode)
- [Issues](https://github.com/fabiocaccamo/django-treenode/issues)
- [License: MIT](https://github.com/fabiocaccamo/django-treenode/blob/main/LICENSE)
- [README](https://github.com/fabiocaccamo/django-treenode/blob/main/README.md)
- [Releases](https://github.com/fabiocaccamo/django-treenode/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/fabiocaccamo-django-treenode
