Adding List-/StructBlocks programmatically

Viewed 33

i'm new to Wagtail and struggling to create content programmatically from a Django management command.

I have defined a StructBlock and Page with a StreamField like so:

class TaskBlock(blocks.StructBlock):
    tasks = blocks.ListBlock(
        blocks.StructBlock(
            [
                ("title", blocks.CharBlock()),
                ("length", blocks.IntegerBlock())
            ]
        )
    )

    class Meta:
        template = "tasks/task_block.html"
        icon = "edit"
        label = "Task"


class TaskPage(Page):
    tasks_first = StreamField([("tasks_first", TaskBlock())], null=True)

    content_panels = Page.content_panels + [StreamFieldPanel("tasks_first")]

My command looks like:

class Command(BaseCommand):
    def handle(self, *args, **options):
        parent_page = Page.objects.get(title="Task Master").specific
        task_page = TaskPage(
            title="Task Page 5",
            slug="task-page-5",
            tasks_first=json.dumps(
                [
                    {
                        "type": "tasks_first",
                        "value": [
                            {
                                "type": "tasks",
                                "value": [
                                    {"type": "title", "value": "this is a task"},
                                    {"type": "length", "value": 5}
                                ],
                            }
                        ],
                    }
                ]
            ),
        )
        parent_page.add_child(instance=task_page)
        revision = task_page.save_revision()
        revision.publish()
        task_page.save()

The page is successfully created with a Task block, but the task is blank and does not contain the test data defined in the json.

I'm probably missing some basic knowledge on how to define the json for the TaskBlock() in the StreamField tasks_first.

Could you give me some guidance?

1 Answers

Probably clearer to start from the inside and work outwards here...

The JSON representation of a StructBlock value is a simple dict, where the keys are the sub-block names and the values are their values - you don't need "type" and "value" here. So, a value for the innermost StructBlock would look like:

{"title": "this is a task", "length": 5}

The value for ListBlock is a list of these values. If we only want one task in the list, this will be:

[
    {"title": "this is a task", "length": 5}
]

TaskBlock is a StructBlock with a single sub-block named "tasks", so again this should be a simple dict:

{
    "tasks": [
        {"title": "this is a task", "length": 5}
    ]
}

And finally we place this in the stream - this is where the "type" and "value" come in.

[
    {
        "type": "tasks_first",
        "value": {
            "tasks": [
                {"title": "this is a task", "length": 5}
            ]
        }
    }
]

This, then, is what you pass to json.dumps(...).

Finally, are you sure you need a StreamField at all here? If you just want a page with a list of tasks on, you could do this with an InlinePanel where each child object is one task.

Related