Skip to main content

wagtail-in-a-tree

Manage any django-treebeard model in the Wagtail admin, as a snippet. Wagtail already depends on treebeard; this package gives your own tree models (categories, themes, regions…) an admin made for trees. Adjacency list (AL_Node), materialized path (MP_Node) and nested sets (NS_Node) models all work, sorted by treebeard (node_order_by) or ordered by hand.

  • Listing: Wagtail's standard snippet listing, with a layout switch next to the search box (like the grid / list switch of the image listing): the top-level nodes only, each linking to its own listing, or the whole tree. Rows are indented by depth, with a chevron to hide or show a node's descendants. Searching, filtering or sorting a column gives the usual flat, paginated listing.
  • A node's own listing (children/<pk>/) shows its whole subtree, with breadcrumbs back up the tree. "Add" from there starts the new node under it.
  • Moving: drag a row onto another one to nest it under it, or onto its top or bottom edge to place it before or after it. Without a mouse, each row's "…" menu has "Move up" and "Move down", and the edit form has treebeard's "Relative to" / "Position" fields.
  • Sorting: "Sort alphabetically" in the header "…" menu (the whole tree, or the subtree of the node being listed) and "Sort sub-items alphabetically" in a row's "…" menu. A confirmation page shows the tree as it will be. Accents and case are ignored.
  • Choosing nodes: many-to-many fields to the model are edited with TreeCheckboxSelectMultiple, checkboxes in a collapsible tree.

Supported versions

This package supports Wagtail 7.4 and up, and all compatible versions of Python and Django, with django-treebeard 5. It is tested from the lowest supported versions (Python 3.11, Django 5.2, Wagtail 7.4, django-treebeard 5.0) to the latest ones.

Installation

The package is not on PyPI yet: install it from the repository. Pick the command for your preferred package installer:

uv add "wagtail-in-a-tree @ git+https://github.com/incubateur-ademe/wagtail-in-a-tree.git"
pip install "wagtail-in-a-tree @ git+https://github.com/incubateur-ademe/wagtail-in-a-tree.git"
INSTALLED_APPS = [
    ...,
    "wagtail_in_a_tree",
    "treebeard",  # for treebeard's own translations ("Relative to", "Child of"…)
]

Usage

The model

from django.db import models
from treebeard.mp_tree import MP_Node
from wagtail.admin.panels import FieldPanel
from wagtail_in_a_tree.forms import TreeNodeForm, tree_panel


class Category(MP_Node):
    name = models.CharField(max_length=80)

    base_form_class = TreeNodeForm  # adds the "Relative to" / "Position" fields
    panels = [FieldPanel("name"), tree_panel()]  # tree_panel(heading="…") to rename it

    def __str__(self):
        return self.name  # labels, breadcrumbs, and the key of "Sort alphabetically"

An adjacency list model needs treebeard's two columns:

from treebeard.al_tree import AL_Node


class Category(AL_Node):
    name = models.CharField(max_length=80)
    parent = models.ForeignKey(
        "self",
        null=True,
        blank=True,
        related_name="children_set",
        on_delete=models.CASCADE,
    )
    sib_order = models.PositiveIntegerField()  # not needed with node_order_by
    ...

sib_order counts from 1 within each group of siblings: treebeard does not make room for a move before a node whose sib_order is 0. When adding the column to a populated table, number the existing rows from 1 in a data migration.

Set node_order_by = ["name"] to let treebeard keep siblings sorted: rows can then only be nested, and the reordering actions (edges of a row, Move up / down, Sort alphabetically) are not offered.

The admin

In wagtail_hooks.py:

from wagtail.snippets.models import register_snippet
from wagtail_in_a_tree.views import TreeSnippetViewSet

from .models import Category


class CategoryViewSet(TreeSnippetViewSet):
    model = Category
    icon = "tag"
    list_display = ["name"]
    search_fields = ["name"]  # the layout switch sits in the header's search form
    add_to_admin_menu = True  # its own sidebar item rather than under Snippets


register_snippet(CategoryViewSet)

TreeSnippetViewSet is a SnippetViewSet: everything it supports still works. Only snippets are supported, not a plain ModelViewSet.

Many-to-many fields to the model

Once the model is registered with TreeSnippetViewSet, every ManyToManyField or ParentalManyToManyField pointing to it gets TreeCheckboxSelectMultiple in Wagtail's admin forms, with no panel change. Choices left out by limit_choices_to are hidden, their ancestors staying as plain labels. Other many-to-many fields (to pages, collections or any other model) are not affected.

To use another widget on one field: FieldPanel("categories", widget=...). To use the tree widget on a model registered otherwise:

from wagtail_in_a_tree.widgets import TreeCheckboxSelectMultiple

FieldPanel("categories", widget=TreeCheckboxSelectMultiple)

Reference

Public API

Import What for
wagtail_in_a_tree.views.TreeSnippetViewSet the snippet viewset to register
wagtail_in_a_tree.forms.TreeNodeForm base_form_class of the model
wagtail_in_a_tree.forms.tree_panel(heading=None) the panel of the move fields
wagtail_in_a_tree.widgets.TreeCheckboxSelectMultiple the tree checkbox widget
wagtail_in_a_tree.tree.sort_alphabetically(model, parent=None) the sort, for scripts and migrations

URLs added to the viewset, next to Wagtail's own:

URL View
children/<pk>/ the subtree of a node
move/ POST node, target, position (child, before or after)
sort/?parent=<pk> confirmation, then POST to sort

Permissions: moving and sorting need the model's "change" permission. Users without it see the listings without drag and drop or move actions.

Customizing: TreeSnippetViewSet takes Wagtail's usual view class attributes (index_view_class, add_view_class…) plus move_view_class and sort_view_class. The templates live under wagtail_in_a_tree/.

Translations: French is included.

Limits

  • Tree listings are not paginated: every node of the tree (or subtree) is on one page, so that any row can be a drop target.
  • Large trees: the "Children" column counts each top-level row with one query, and sorting moves one node at a time. This suits trees of a few hundred nodes, not page-tree sizes.
  • Translatable models (TranslatableMixin): listings follow the locale selected in the listing, and "Relative to" only offers nodes of the node's locale. Sorting orders every group of siblings, whatever its locale.

Contributing

See CONTRIBUTING.md: just demo runs a test project with an AL, an MP and an NS tree, just test the test suite.

License

MIT.

Metadata

Release files for wagtail-in-a-tree 0.1.0

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

Source distribution (sdist)

Source distribution for wagtail-in-a-tree 0.1.0
File Size Uploaded Provenance Actions
wagtail_in_a_tree-0.1.0.tar.gz 20.6 kB With provenance Details for wagtail_in_a_tree-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wagtail-in-a-tree 0.1.0
File Interpreter ABI Platform Actions
wagtail_in_a_tree-0.1.0-py3-none-any.whl Python 3 none any Details for wagtail_in_a_tree-0.1.0-py3-none-any.whl

Total release size: 50.0 kB

Release files / wagtail_in_a_tree-0.1.0.tar.gz

Download URL wagtail_in_a_tree-0.1.0.tar.gz
Size 20.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f710835cd40a38e06540d49be6f62ac44ea49d2dc01e2da07cc0e18206bb758d
BLAKE2b-256 checksum
How to use checksums
974aa169c5720e6d3263834bc5a7875aea57aa1a4c6f871bda64c3b8cb7084ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / wagtail_in_a_tree-0.1.0-py3-none-any.whl

Download URL wagtail_in_a_tree-0.1.0-py3-none-any.whl
Size 29.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
caba0e33b926b0e1659150150e6e22c49d845d2c116c9016d1cecca1acbdb481
BLAKE2b-256 checksum
How to use checksums
ba3cab2fd76c69ffd6d8ac63c0567d9f05e1c318109736884513912405fa291f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Security for wagtail-in-a-tree 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for wagtail_in_a_tree-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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