I have a use case where we need dynamic blocks for a StreamField
. Unfortunately this doesn't seem possible, since block types are specified in the model's StreamField
field.
I dug through the FieldPanel, StreamBlock and GroupPanel code and cannot clearly find a way to override the available blocks for the StreamField outside the model definition.
This is my current admin EditView
, where you can see what I'm trying to accomplish (see the NOTE/FIXME comments below:
class EditProductView(EditView):
def get_edit_handler(self):
summary_panels = [
FieldPanel('title'),
FieldPanel('description'),
FieldPanel('body'),
]
attributes_panel = [
# NOTE/FIXME: This is where we need the custom blocks to load, based
# on `self.instance.product_type`.
FieldPanel('attributes'),
# StreamFieldPanel (which is deprecated) does not work because you can
# only set the blocks within the model, when defining the StreamFieldPanel.
# I would love to be able to do something like:
StreamFieldPanel('attributes', block_types=[
('test', blocks.CharBlock()), # Like this for example
])
]
settings_panels = [
FieldPanel('categories'),
StateMachinePanel('state', sm_prop='sm', heading="State"),
]
return TabbedInterface([
ObjectList(summary_panels, heading='Summary'),
ObjectList(attributes_panel, heading='Attributes'),
ObjectList(settings_panels, heading='Settings'),
]).bind_to_model(self.model_admin.model)
Is it possible to override available StreamField blocks in this way, or are there potential issues with json_field
DB integrity?
This wouldn't work, because the StreamField block definition isn't just used for the edit form - any time you access that field, even just to output it on a template, the StreamField needs to look at the block definition to unpack the JSON and return the appropriate Python objects. For example, suppose the database contained:
[{'type': 'test', 'value': 123}]
What should page.attributes[0].value
return? If the test
block is of type IntegerBlock
, it would return 123
- but if it was a PageChooserBlock
instead, it would return the Page
instance with ID 123. If it was an ImageChooserBlock
, it would return the Image
instance with ID 123, and so on. In your example, the only way to find out the block type is to call get_edit_handler
and construct the entire editing interface as a side effect - this would be mixing up model-level logic with admin interface code, and be massively inefficient just for accessing a single field.