0

I have defined a custom StructBlock with a StreamField allowing StructBlock instances, such as follows:

class NavigationPageChooserBlock(wagtail_blocks.StructBlock):
    title = wagtail_blocks.CharBlock()
    page = wagtail_blocks.PageChooserBlock()


class NavigationDropdownMenuBlock(wagtail_blocks.StructBlock):
    title = wagtail_blocks.CharBlock()
    menu_items = wagtail_blocks.StreamBlock(
        [
            ("page", NavigationPageChooserBlock()),
        ]
    )

For full context, the blocks are used as part of a custom NavigationMenuSetting that I need to use in a content importer from a Drupal site.

@register_setting
class NavigationMenuSetting(BaseSiteSetting):
    menu_items = StreamField(
        [
            ("drop_down", NavigationDropdownMenuBlock()),
        ],
        use_json_field=True,
    )

I'm having a lot of difficulty trying to figure out how to create a NavigationDropdownMenuBlock instance with child menu_items in Python code. I've tried the following, but get an error.

/wagtail/blocks/stream_block.py", line 290, in get_prep_value
    return value.get_prep_value()
           ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'get_prep_value'

Here is my importer code:

# example_page is created previously

# NavigationDropdownMenuBlock (StructBlock)
example_dropdown = {
    "title": "Dropdown Menu",
    # StreamBlock
    "menu_items": [
        (
            "page",
            # NavigationPageChooserBlock (StructBlock)
            {
                "title": "Example Page Link",
                "page": example_page,
            },
        ),
    ],
}

navigation_items = [
    ("drop_down", example_dropdown),
]
navigation_menu = NavigationMenuSetting(
    menu_items=navigation_items,
    site_id=1,
)

navigation_menu.save()

I've also tried using StreamValue, but can't figure out what to use for the string_block attribute:

navigation_dropdown = NavigationDropdownMenuBlock(
    title="Dropdown Menu",
)

navigation_dropdown.menu_items = StreamValue(
    stream_block=navigation_dropdown.menu_items.stream_block,
    stream_data=[
        {
            "type": "page",
            # StructBlock
            "value": {
                "title": "Example Page Link",
                "page": example_page,
            },
        },
    ],
)
stream_block=navigation_dropdown.menu_items.stream_block,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NavigationDropdownMenuBlock' object has no attribute 'menu_items'

How can I change my import script to properly create the example_dropdown item for the NavigationMenuSetting.menu_items?

Brylie Christopher Oxley
  • 1,684
  • 1
  • 17
  • 34
  • This question is similar to another question, linked below, but the answer is really not so clear since I am working directly with blocks and not a Page. https://stackoverflow.com/questions/46795866/add-streamblock-child-items-programmatically-in-wagtail?rq=1 – Brylie Christopher Oxley Feb 23 '23 at 18:26
  • I'm trying really hard to figure this out, but there is very little I can find in the documentation or on StackOverflow. This is about the most frustrating thing I've encountered when working with Wagtail. – Brylie Christopher Oxley Feb 23 '23 at 18:34

1 Answers1

0

Did you see the Wagtail doc on modifying streamfields? https://docs.wagtail.org/en/stable/topics/streamfield.html#modifying-streamfield-data

If you've used BeautifulSoup before, it works a little similarly: create a tag then append/insert at the appropriate place. You can't just add blocks via the raw data as the UID's would be missing from the blocks, Wagtail wouldn't know what to do with them.

You would need to create NavigationPageChooserBlock instances for each NavigationDropdownMenuBlock instance you need to create, then append those to your NavigationMenuSetting.menu_items field.

Is there a reason for using a Streamfield and not a clusterable model with orderables? This would make your life a lot easier.