Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ Features and limitations
- The fields added by the common table expression always are
``tree_depth``, ``tree_path`` and ``tree_ordering``. The names cannot
be changed. ``tree_depth`` is an integer, ``tree_path`` an array of
primary keys and ``tree_ordering`` an array of values used for
ordering nodes within their siblings. Note that the contents of the
``tree_path`` and ``tree_ordering`` are subject to change. You shouldn't rely
on their contents.
primary keys representing the path from the root to the current node
(including the current node itself), and ``tree_ordering`` an array of
values used for ordering nodes within their siblings at each level of
the tree hierarchy. With UUID primary keys and no explicit ordering
specified, both fields may contain the same UUID values since siblings
are ordered by their primary key by default. Note that the contents of
the ``tree_path`` and ``tree_ordering`` are subject to change. You
shouldn't rely on their contents.
- Besides adding the fields mentioned above the package only adds queryset
methods for ordering siblings and filtering ancestors and descendants. Other
features may be useful, but will not be added to the package just because
Expand Down Expand Up @@ -182,6 +186,57 @@ Basic usage
nodes = Node.objects.with_tree_fields().without_tree_fields()


Understanding tree fields
-------------------------

When using ``with_tree_fields()``, each node gets three additional attributes:

- **``tree_depth``**: An integer representing the depth of the node in the tree
(root nodes have depth 0)
- **``tree_path``**: An array containing the primary keys of all ancestors plus
the current node itself, representing the path from root to current node
- **``tree_ordering``**: An array containing the ordering/ranking values used
for sibling ordering at each level of the tree hierarchy

The key difference between ``tree_path`` and ``tree_ordering``:

.. code-block:: python

# Example tree structure:
# Root (pk=1, order=0)
# ├── Child A (pk=2, order=10)
# │ └── Grandchild (pk=4, order=5)
# └── Child B (pk=3, order=20)

# For the Grandchild node:
grandchild = Node.objects.with_tree_fields().get(pk=4)

# tree_path shows the route through primary keys: Root -> Child A -> Grandchild
assert grandchild.tree_path == [1, 2, 4] # [root.pk, child_a.pk, grandchild.pk]

# tree_ordering shows ordering values at each level: Root's order, Child A's order, Grandchild's order
assert grandchild.tree_ordering == [0, 10, 5] # [root.order, child_a.order, grandchild.order]

**Important note for UUID primary keys**: When using UUID primary keys without
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't just apply for UUID primary keys, but for all models without a sibling ordering field.

explicit ordering (like a ``position`` field), siblings are ordered by their
primary key by default. This means ``tree_path`` and ``tree_ordering`` will
contain the same UUID values, which can be confusing. To make the distinction
clearer, consider adding an explicit ordering field:

.. code-block:: python

class Node(TreeNode):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=100)
position = models.PositiveIntegerField(default=0)

class Meta:
ordering = ['position']

With explicit ordering, ``tree_path`` will contain UUIDs while ``tree_ordering``
will contain the position values used for ordering.


Filtering tree subsets
----------------------

Expand Down