Model Inheritance and Extensions¶
Model Inheritance¶
Creating your own tree model is very easy. Simply inherit from TreeNodeModel. Below is an example of a basic category model:
models.py
from django.db import models
from treenode.models import TreeNodeModel
class Category(TreeNodeModel):
treenode_display_field = "name" # Defines the field used for display in the admin panel
name = models.CharField(max_length=50)
class Meta(TreeNodeModel.Meta): # Preserve TreeNodeModel's indexing settings
verbose_name = "Category"
verbose_name_plural = "Categories"
Important
Always specify TreeNodeModel.Meta as the parent of your model’s Meta class. Failing to do so will result in incorrect database indexing and other negative consequences.
Class and Instance Attributes¶
This section describes the class and instance attributes available when interacting with a tree model. Understanding these attributes will help you avoid subtle bugs and write cleaner, more efficient code.
node.parent¶
The core field of any tree node model is the parent field. It is a ForeignKey that establishes a many-to-one relationship between nodes, forming the tree hierarchy.
Tip
Although the parent field is always up-to-date and can be accessed directly, it is good practice to use the get_parent() and set_parent() methods for better consistency.
cls.treenode_display_field¶
Defines the field used to display nodes in the Django admin panel. If not set, nodes are shown generically as "Node <id>".
cls.sorting_field¶
Specifies the field used to sort sibling nodes. Defaults to "priority", but can be customized.
Warning
The priority field exists on each model instance. Although it is accessible, you should never read or set its value directly.
Due to internal caching mechanisms, the visible value might not match the actual database value. Using refresh_from_db() is expensive and clears model caches. Instead, always use the get_priority() and set_priority() methods.
If you specify another field, for example name, the tree will be sorted by that field alphabetically:
class Category(TreeNodeModel):
sorting_field = "name"
...
cls.sorting_direction¶
An optional attribute that controls the default sorting order (ascending or descending) for siblings.
It must be set to a value from the SortingChoices class.
Other internal attributes should not be modified directly. Use the provided public methods instead.
cls.api_login_required¶
Each model in the tree can define the api_login_required class attribute. The True value enables API access control for each model via Django's login system (login_required). In this case, all API endpoints for the model will require the user to be logged in.
class Category(TreeNodeModel):
api_login_required = False
...
Warning
If api_login_required attribute is not explicitly defined, the API for the model remains open by default.
In production environments, API endpoints must not remain open.
Built-in Classes¶
SortingChoices Class¶
SortingChoices is a built-in helper class defined inside TreeNodeModel. It provides clear constants for sorting direction:
| Value | Meaning |
|---|---|
SortingChoices.ASC |
Sort ascending (lowest to highest priority). Default. |
SortingChoices.DESC |
Sort descending (highest to lowest priority). |
You can reference it inside your model:
class Category(TreeNodeModel):
sorting_direction = SortingChoices.DESC
Since SortingChoices is attached to the model class, no separate import is required.
Meta Class¶
When extending TreeNodeModel, always inherit from TreeNodeModel.Meta to preserve essential database indexing.
To add your own indexes:
class Category(TreeNodeModel):
treenode_display_field = "name"
name = models.CharField(max_length=50)
class Meta(TreeNodeModel.Meta):
verbose_name = "Category"
verbose_name_plural = "Categories"
indexes = list(TreeNodeModel._meta.indexes) + [
models.Index(fields=["name"]),
]
This approach ensures full Django integration, efficient queries, and correct hierarchical behavior.
Important
Do not override Meta.ordering in models based on TreeNodeModel.
Internal ordering relies on the _path field to keep the hierarchy intact
in the admin interface and elsewhere. To control sibling order, use the
sorting_field attribute instead.
Note
Canonical tree order in TreeNode is _path (with id used only as a
deterministic SQL tie-breaker where required by low-level queries).
Model Extending¶
Extending TreeNodeModel is flexible and straightforward. You can freely add custom fields and methods.
Example:
class Category(TreeNodeModel):
treenode_display_field = "name"
sorting_field = "name"
sorting_direction = SortingChoices.DESC
name = models.CharField(max_length=50)
code = models.CharField(max_length=5)
class Meta(TreeNodeModel.Meta):
verbose_name = "Category"
verbose_name_plural = "Categories"
def get_full_string(self):
return f"{self.code} - {self.name}"
The framework places no restrictions on functionality expansion.
Model Manager¶
When creating a custom model manager, always extend TreeNodeModelManager instead of modifying internal components like TreeNodeQuerySet.
Example of a safe custom manager:
from treenode.managers import TreeNodeModelManager
class CustomTreeNodeManager(TreeNodeModelManager):
def active(self):
"""Return only active nodes."""
return self.get_queryset().filter(is_active=True)
Or if you want to override get_queryset() itself:
class CustomTreeNodeManager(TreeNodeModelManager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
Following these principles ensures your managers remain safe, future-proof, and compatible with the tree structure.