wagtailwagtail-streamfield

Insert nested StreamChild into StreamField programmatically


Background

I try to import some datas from a wordpress website into a new Wagtail one. I made a script that iterate on my data from wordpress and I check the type and then try to transform data into a StreamChild/StructBlock to insert it into my body which is a StreamField.

Problem

I don't get how to transform a certain block that contains a StreamBlock itself.

This is my block definition :

class InternalLinkBlock(blocks.StructBlock):
    page = PageChooserBlock()
    title = blocks.CharBlock()


class ExternalLinkBlock(blocks.StructBlock):
    link = blocks.URLBlock()
    title = blocks.CharBlock()


class LinkStreamBlock(blocks.StreamBlock):
    internal = InternalLinkBlock()
    external = ExternalLinkBlock()

class ButtonBlock(block.StructBlock):
    title = block.CharBlock()
    cta = LinkStreamBlock()
    with_background = blocks.BooleanBlock()

class StoryBlock(blocks.StreamBlock):
    button = ButtonBlock()

And how I try to insert it in my script :

    if block["blockName"] == "button_block":
        new_block = (
            "button",
            {
                "title": "coucou",
                "with_background": True,
                "cta": [(
                    "external",
                    {"link": "google.com", "title": "cta_text"}),
                ]
            },
        )
        page.body.append(new_block)
    new_page = page.save_revision()

I get this error in save : AttributeError: 'tuple' object has no attribute 'get_prep_value'

I can't figure out how to insert my data. Can you help me please?


Solution

  • This is due to an oversight that has been fixed for the forthcoming Wagtail 6.5 release - appending to a StreamField is supposed to accept any reasonable data type (such as the "list of (block type, value) tuples" representation for a StreamBlock), but currently only accepts the specific data type used internally by the block - which for StreamBlock is an instance of the custom StreamValue class. Your code should hopefully work for Wagtail 6.5 onward.

    As a workaround, you can modify your code to pass "cta" as a StreamValue instance:

    from wagtail.blocks import StreamValue
    
    cta_block_def = ButtonBlock.base_blocks["cta"]
    
    # ...
    
        if block["blockName"] == "button_block":
            new_block = (
                "button",
                {
                    "title": "coucou",
                    "with_background": True,
                    "cta": StreamValue(
                        cta_block_def,
                        [(
                            "external",
                            {"link": "google.com", "title": "cta_text"}),
                        ],
                    ),
                },
            )
            page.body.append(new_block)
        new_page = page.save_revision()