Typed table block¶
The typed_table_block
module provides a StreamField block type for building tables consisting of mixed data types. Developers can specify a set of block types (such as RichTextBlock
or FloatBlock
) to be available as column types; page authors can then build up tables of any size by choosing column types from that list, in much the same way that they would insert blocks into a StreamField. Within each column, authors enter data using the standard editing control for that field (such as the Draftail editor for rich text cells).
Installation¶
Add "wagtail.contrib.typed_table_block"
to your INSTALLED_APPS
:
INSTALLED_APPS = [
...
"wagtail.contrib.typed_table_block",
]
Usage¶
TypedTableBlock
can be imported from the module wagtail.contrib.typed_table_block.blocks
and used within a StreamField definition. Just like StructBlock
and StreamBlock
, it accepts a list of (name, block_type)
tuples to use as child blocks:
from wagtail.contrib.typed_table_block.blocks import TypedTableBlock
from wagtail import blocks
from wagtail.images.blocks import ImageChooserBlock
class DemoStreamBlock(blocks.StreamBlock):
title = blocks.CharBlock()
paragraph = blocks.RichTextBlock()
table = TypedTableBlock([
('text', blocks.CharBlock()),
('numeric', blocks.FloatBlock()),
('rich_text', blocks.RichTextBlock()),
('image', ImageChooserBlock())
])
To keep the UI as simple as possible for authors, it’s generally recommended to use Wagtail’s basic built-in block types as column types, as above. However, all custom block types and parameters are supported. For example, to define a ‘country’ column type consisting of a dropdown of country choices:
table = TypedTableBlock([
('text', blocks.CharBlock()),
('numeric', blocks.FloatBlock()),
('rich_text', blocks.RichTextBlock()),
('image', ImageChooserBlock()),
('country', ChoiceBlock(choices=[
('be', 'Belgium'),
('fr', 'France'),
('de', 'Germany'),
('nl', 'Netherlands'),
('pl', 'Poland'),
('uk', 'United Kingdom'),
])),
])
On your page template, the {% include_block %}
tag (called on either the individual block, or the StreamField value as a whole) will render any typed table blocks as an HTML <table>
element.
{% load wagtailcore_tags %}
{% include_block page.body %}
Or:
{% load wagtailcore_tags %}
{% for block in page.body %}
{% if block.block_type == 'table' %}
{% include_block block %}
{% else %}
{# rendering for other block types #}
{% endif %}
{% endfor %}
Custom validation¶
As with other blocks, validation logic on TypedTableBlock
can be customised by overriding the clean
method (see StreamField validation). Raising a ValidationError
exception from this method will attach the error message to the table as a whole. To attach errors to individual cells, the exception class wagtail.contrib.typed_table_block.blocks.TypedTableBlockValidationError
can be used - in addition to the standard non_block_errors
argument, this accepts a cell_errors
argument consisting of a nested dict structure where the outer keys are row indexes and the inner keys are column indexes. For example:
from django.core.exceptions import ValidationError
from wagtail.blocks import IntegerBlock
from wagtail.contrib.typed_table_block.blocks import TypedTableBlock, TypedTableBlockValidationError
class LuckyTableBlock(TypedTableBlock):
number = IntegerBlock()
def clean(self, value):
result = super().clean(value)
errors = {}
print(result.row_data)
for row_num, row in enumerate(result.row_data):
row_errors = {}
for col_num, cell in enumerate(row['values']):
if cell == 13:
row_errors[col_num] = ValidationError("Table cannot contain the number 13")
if row_errors:
errors[row_num] = row_errors
if errors:
raise TypedTableBlockValidationError(cell_errors=errors)
return result