Wagtail: It's better to make every block a StructBlock (but it shouldn't be)

Created on 1 Feb 2016  路  2Comments  路  Source: wagtail/wagtail

Sometimes I start out with code that looks like this:

cards = blocks.ListBlock(blocks.StructBlock([
    ('title', blocks.CharBlock()),
    ('text', blocks.TextBlock())
]), icon="fa-clone", template="blocks/cards.html")

Then by the end of the project, it ends up looking like this:

cards = blocks.StructBlock([
    ('bg_color', blocks.ChoiceBlock(choices=[
        ('red', 'Red'),
        ('green', 'Green'),
        ('blue', 'Blue')
    ])),
    ('cards', blocks.ListBlock(blocks.StructBlock([
        ('title', blocks.CharBlock()),
        ('text', blocks.TextBlock())
    ]))),
], icon="fa-clone", template="blocks/cards.html")

It turns out that the top-level block itself needed extra configuration, so I had to put the list of cards inside a child element. But every time I restructure a StreamField block like this, it erases all the data from the site...

There's really no downside of using a StructBlock wrapper with a single field, so as it turns out, it's a lot more safe and rational to instead create my block like this right from the start:

cards = blocks.StructBlock([
    ('cards', blocks.ListBlock(blocks.StructBlock([
        ('title', blocks.CharBlock()),
        ('text', blocks.TextBlock())
    ]))),
], icon="fa-clone", template="blocks/cards.html")

So the problem is: it's better to make every block a StructBlock when that shouldn't be the case. This means the API could be improved. I'm not sure right now what that would look like, but I wanted to leave this here since I've run into it a few times for the project I'm working on.

CleanuOptimisation

Most helpful comment

I have an idea for how to handle this.

We could add a config field to structural block types which contain their own fields. So you'd have:

  • block_type
  • value
  • config

This way we are separating the configuration of a block from the content of a block.

This is beneficial when you have, say, a listblock of cards. You could template it like this

<div class="cards" style="background: {{ self.config.color }}">
  {% for card in self.value %}
    <div class="card">
      <h1>{{ card.title }}</h1>
      <p>{{ card.text }}</p>
    </div>
  {% endfor %}
</div>

It might not be exactly like that but maybe you get the point.

All 2 comments

I have an idea for how to handle this.

We could add a config field to structural block types which contain their own fields. So you'd have:

  • block_type
  • value
  • config

This way we are separating the configuration of a block from the content of a block.

This is beneficial when you have, say, a listblock of cards. You could template it like this

<div class="cards" style="background: {{ self.config.color }}">
  {% for card in self.value %}
    <div class="card">
      <h1>{{ card.title }}</h1>
      <p>{{ card.text }}</p>
    </div>
  {% endfor %}
</div>

It might not be exactly like that but maybe you get the point.

:+1:

Was this page helpful?
0 / 5 - 0 ratings