wagtailwagtail-streamfield

Using the same StreamField multiple times within a MultiFieldPanel


I have divided a page into sections and I'm trying to use the same StreamField so I don't repeat code but having the same one in different MultiFieldPanel's as per my code below makes it not save correctly, after saving draft or publishing the page the fields go blank. I'm trying to figure out the correct way to set this up.

    top_heading = models.CharField(max_length=50, null=True, blank=True)
    middle_heading = models.CharField(max_length=50, null=True, blank=True)
    bottom_heading = models.CharField(max_length=50, null=True, blank=True)
    center_cards = StreamField([
        ('centercardsblock', general_blocks.CenterCardsBlock()),
    ],  
    block_counts={
        'centercardsblock': {'max_num': 4},
    },
        null=True, 
        blank=True,
        use_json_field=True,
    )

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel('top_heading'),
                FieldPanel('center_cards'),
            ],
            heading='Top Section'
        ),

        MultiFieldPanel(
            [
                FieldPanel('middle_heading'),
                FieldPanel('center_cards'),
            ],
            heading='Middle Section'
        ),

        MultiFieldPanel(
            [
                FieldPanel('bottom_heading'),
                FieldPanel('center_cards'),
            ],
            heading='Bottom Section'
        ),
    ]

I'm guessing the below will work but I want to try not repeat code.

top_center_cards = StreamField([
        ('centercardsblock', general_blocks.CenterCardsBlock()),
    ],  
    block_counts={
        'centercardsblock': {'max_num': 4},
    },
        null=True, 
        blank=True,
        use_json_field=True,
    )

middle_center_cards = StreamField([
        ('centercardsblock', general_blocks.CenterCardsBlock()),
    ],  
    block_counts={
        'centercardsblock': {'max_num': 4},
    },
        null=True, 
        blank=True,
        use_json_field=True,
    )

bottom_center_cards = StreamField([
        ('centercardsblock', general_blocks.CenterCardsBlock()),
    ],  
    block_counts={
        'centercardsblock': {'max_num': 4},
    },
        null=True, 
        blank=True,
        use_json_field=True,
    )

Solution

  • You do need one StreamField declaration for each time the StreamField appears on the page. However, if you don't want to repeat the block definition, you can define that as a subclass of StreamBlock:

    class CenterCardsStreamBlock(blocks.StreamBlock):
        centercardsblock = general_blocks.CenterCardsBlock()
    
        class Meta:
            block_counts = {
                'centercardsblock': {'max_num': 4},
            }
    

    and then use that in the StreamField definition rather than repeating the list.

        top_center_cards = StreamField(
            CenterCardsStreamBlock(),
            null=True, blank=True, use_json_field=True
        )
    
        middle_center_cards = StreamField(
            CenterCardsStreamBlock(),
            null=True, blank=True, use_json_field=True
        )
    
        bottom_center_cards = StreamField(
            CenterCardsStreamBlock(),
            null=True, blank=True, use_json_field=True
        )