About Creating Django-treebeard Hierarchies in Data Migrations

Django-treebeard API is not accessible in Django migrations, therefore, you would need to use low-level concepts to create a hierarchical tree in a data migration.

When you use Materialized Paths for the hierarchical models, the model could look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# categories/models.py
from django.db import models
from treebeard.mp_tree import MP_Node

class Category(MP_Node):
    title = models.CharField("Title", max_length=255)

    class Meta:
        verbose_name = "Category"
        verbose_name_plural = "Categories"

    def __str__(self):
        return self.title

And the migration creating a category tree on the fly could look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# categories/migrations/0002_create_categories.py
from django.db import migrations

def create_categories(apps, schema_editor):
    from treebeard.numconv import NumConv

    alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    number_converter = NumConv(radix=len(alphabet), alphabet=alphabet)

    category_tree = [
        {
            "title": "Fruits",
            "children": [
                {"title": "Apples"},
                {"title": "Bananas"},
            ],
        },
        {
            "title": "Vegetables",
            "children": [
                {"title": "Potatoes"},
                {"title": "Carrots"},
            ],
        },
    ]

    Category = apps.get_model("categories", "Category")

    for root_index, root_category_dict in enumerate(category_tree, 1):
        root = Category.objects.create(
            path=f"{number_converter.int2str(root_index):0>4}",
            depth=1,
            numchild=0,
            title=root_category_dict["title"],
        )
        for leaf_index, leaf_category_dict in enumerate(
            root_category_dict["children"], 1
        ):
            leaf = Category.objects.create(
                path=(
                    f"{number_converter.int2str(root_index):0>4}"
                    f"{number_converter.int2str(leaf_index):0>4}"
                ),
                depth=2,
                numchild=0,
                title=leaf_category_dict["title"],
            )
            root.numchild += 1
            root.save(update_fields=["numchild"])


def dummy(apps, schema_editor):
    pass


class Migration(migrations.Migration):

    dependencies = [
        ("categories", "0001_initial"),
    ]

    operations = [
        migrations.RunPython(create_categories, dummy),
    ]

Here the paths for the categories will be like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
0001
00010001
00010002
...
0002
...
0009
000A
000B
...

Tips and Tricks Programming Databases Django 5.x Django 4.2 Django 3.2 Python 3 django-treebeard