Understanding Wagtail’s flexible content block system in the Bakery Demo
StreamField is one of Wagtail’s most powerful features, allowing editors to compose flexible page layouts using reusable content blocks. The Bakery Demo demonstrates various StreamField patterns from basic to advanced.
StreamField is a Django field type that stores structured content as a sequence of blocks. Each block has a specific type (heading, paragraph, image, etc.) and can be added, removed, or reordered by editors.
from wagtail.fields import StreamFieldfrom bakerydemo.base.blocks import BaseStreamBlockclass BlogPage(Page): body = StreamField( BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True )
use_json_field=True stores StreamField data as JSON in the database, which is more efficient than the legacy format.
The BaseStreamBlock defined in bakerydemo/base/blocks.py is the foundation for most pages:
from wagtail.blocks import ( CharBlock, RichTextBlock, StreamBlock, StructBlock,)from wagtail.embeds.blocks import EmbedBlockfrom wagtail.images.blocks import ImageChooserBlockclass BaseStreamBlock(StreamBlock): """Define the custom blocks that StreamField will utilize.""" heading_block = HeadingBlock() paragraph_block = RichTextBlock( icon="pilcrow", template="blocks/paragraph_block.html", preview_value=""" <h2>Our bread pledge</h2> <p>As a bakery, <b>breads</b> have <i>always</i> been in our hearts. <a href="https://en.wikipedia.org/wiki/Staple_food">Staple foods</a> are essential for society, and – bread is the tastiest of all.</p> """, description="A rich text paragraph", ) image_block = CaptionedImageBlock() block_quote = BlockQuote() embed_block = EmbedBlock( help_text="Insert an embed URL e.g https://www.youtube.com/watch?v=SGJFWirQ3ks", icon="media", template="blocks/embed_block.html", preview_value="https://www.youtube.com/watch?v=mwrGSfiB1Mg", description="An embedded video or other media", )
Block Configuration Options
icon: Icon to display in the admin interface
template: Template file for rendering the block
preview_value: Sample data for the Wagtail styleguide
class ThemeSettingsBlock(StructBlock): theme = ChoiceBlock( choices=[ ("default", "Default"), ("highlight", "Highlight"), ], required=False, default="default", ) text_size = ChoiceBlock( choices=[ ("default", "Default"), ("large", "Large"), ], required=False, default="default", ) class Meta: icon = "cog" label_format = "Theme: {theme}, Text size: {text_size}"class BlockQuote(StructBlock): """Custom StructBlock that allows the user to attribute a quote to the author.""" text = TextBlock() attribute_name = CharBlock( blank=True, required=False, label="e.g. Mary Berry" ) settings = ThemeSettingsBlock(collapsed=True) class Meta: icon = "openquote" template = "blocks/blockquote.html" preview_value = { "text": ( "If you read a lot you're well read / " "If you eat a lot you're well bread." ), "attribute_name": "Willie Wagtail", } description = "A quote with an optional attribution"
When adding StreamField to a model, you can configure its behavior:
class RecipePage(Page): backstory = StreamField( BaseStreamBlock(), # Limit certain block types block_counts={ "heading_block": {"max_num": 1}, "image_block": {"max_num": 1}, "embed_block": {"max_num": 1}, }, blank=True, use_json_field=True, help_text="Use only a minimum number of headings and large blocks.", )
StreamField Parameters
block_types: The StreamBlock class defining available blocks
block_counts: Dict limiting how many of each block type can be added