Gianluca Esposito
Gianluca Esposito

Reputation: 83

add/modify blocks value in wagtail streamfield from shell

I have a page with one StreamField body and a custom block named HeadingBlock:

class HeadingBlock(blocks.StructBlock):
    heading = blocks.CharBlock()
    cssid = blocks.CharBlock()
    ...

class CustomPage(Page):
    ...
    body = StreamField([
        ('heading', HeadingBlock()),
    ...

I need to add new charblock to heading attribute of HeadingBlock, but using shell:

>>> from custom.models import CustomPage
>>> c = CustomPage.objects.all()[0]
>>> c.body[0].heading.value = "hello world" ??? this does not work

Anyone can help? thanks a lot!

EDIT: I simplified the HeadingBlock, removing ListBlock and tried:

>>> c.body[0].value
StructValue([('heading', u'hi'), ('cssid', u'man')])
>>> type(c.body[0].value)
<class 'wagtail.wagtailcore.blocks.struct_block.StructValue'>
>>> from wagtail.wagtailcore.blocks.struct_block import StructValue
>>> c.body[0].value = StructValue([('heading', u'hello'), ('cssid', u'world')])
>>> c.save()

but when i go in admin interface, the fields are empty. I tried:

>>> c.body[0].block.child_blocks
OrderedDict([('heading', <wagtail.wagtailcore.blocks.field_block.CharBlock object at 0x7f4c2aaf9790>), ('cssid', <wagtail.wagtailcore.blocks.field_block.CharBlock object at 0x7f4c2aaf9a90>)])
>>> c.body[0].block.child_blocks['heading']
<wagtail.wagtailcore.blocks.field_block.CharBlock object at 0x7f4c2aaf9790>
>>> c.body[0].block.child_blocks['heading'].value
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'CharBlock' object has no attribute 'value'

Nothing happen, i do not think this is so difficult :-|

Upvotes: 8

Views: 3119

Answers (2)

Eric Sherman
Eric Sherman

Reputation: 23

It's worth mentioning that this behavior has been updated in newer versions of Wagtail:

https://github.com/wagtail/wagtail/pull/6485

https://docs.wagtail.org/en/stable/topics/streamfield.html#modifying-streamfield-data

Upvotes: 2

gasman
gasman

Reputation: 25317

The StructValue([('heading', u'hi'), ('cssid', u'man')]) output is a bit misleading - to construct your own StructValue, you need to pass the corresponding StructBlock object. Another problem you may run into is that the value of a StreamField is not designed to be updated 'in place' as in c.body[0].value (although this may change in a future Wagtail release).

The recommended way to update a StreamField is to construct a new value, consisting of a list of (block_type, value) tuples. When you use this approach, the StreamField will take care of converting values to the right type - so for a StructBlock, you can simply pass in a dict rather than building your own StructValue:

c.body = [
    ('heading', {'heading': 'hi', 'cssid': 'man'})
]

If there's existing data in the field that you want to keep, a more complete code snippet might look something like this:

new_body = []
for block_type, value in c.body:
    if block_type == 'heading':
        new_body.append(
            ('heading', {'heading': 'hello world', 'cssid': value['cssid']})
        )
    else:
        new_body.append((block_type, value))

c.body = new_body

Upvotes: 11

Related Questions