mirror of
https://github.com/wagtail/wagtail.git
synced 2024-12-01 11:41:20 +01:00
Implement BlockDefinitionLookup
This commit is contained in:
parent
91174228d0
commit
b5bf60da06
@ -97,6 +97,17 @@ class Block(metaclass=BaseBlock):
|
|||||||
|
|
||||||
self.label = self.meta.label or ""
|
self.label = self.meta.label or ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def construct_from_lookup(cls, lookup, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
See `wagtail.blocks.definition_lookup.BlockDefinitionLookup`.
|
||||||
|
Construct a block instance from the provided arguments, using the given BlockDefinitionLookup
|
||||||
|
object to perform any necessary lookups.
|
||||||
|
"""
|
||||||
|
# In the base implementation, no lookups take place - args / kwargs are passed
|
||||||
|
# on to the constructor as-is
|
||||||
|
return cls(*args, **kwargs)
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
if not self.meta.label:
|
if not self.meta.label:
|
||||||
|
47
wagtail/blocks/definition_lookup.py
Normal file
47
wagtail/blocks/definition_lookup.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
|
||||||
|
class BlockDefinitionLookup:
|
||||||
|
"""
|
||||||
|
A utility for constructing StreamField Block objects in migrations, starting from
|
||||||
|
a compact representation that avoids repeating the same definition whenever a
|
||||||
|
block is re-used in multiple places over the block definition tree.
|
||||||
|
|
||||||
|
The underlying data is a list of block definitions, such as:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
("wagtail.blocks.CharBlock", [], {"required": True}),
|
||||||
|
("wagtail.blocks.RichTextBlock", [], {}),
|
||||||
|
("wagtail.blocks.StreamBlock", [
|
||||||
|
[
|
||||||
|
("heading", 0),
|
||||||
|
("paragraph", 1),
|
||||||
|
],
|
||||||
|
], {}),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
where each definition is a tuple of (module_path, args, kwargs) similar to that
|
||||||
|
returned by `deconstruct` - with the difference that any block objects appearing
|
||||||
|
in args / kwargs may be substituted with an index into the lookup table that
|
||||||
|
points to that block's definition. Any block class that wants to support such
|
||||||
|
substitutions should implement a static/class method
|
||||||
|
`construct_from_lookup(lookup, *args, **kwargs)`, where `lookup` is
|
||||||
|
the `BlockDefinitionLookup` instance. The method should return a block instance
|
||||||
|
constructed from the provided arguments (after performing any lookups).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, blocks):
|
||||||
|
self.blocks = blocks
|
||||||
|
self.block_classes = {}
|
||||||
|
|
||||||
|
def get_block(self, index):
|
||||||
|
path, args, kwargs = self.blocks[index]
|
||||||
|
try:
|
||||||
|
cls = self.block_classes[path]
|
||||||
|
except KeyError:
|
||||||
|
module_name, class_name = path.rsplit(".", 1)
|
||||||
|
module = import_module(module_name)
|
||||||
|
cls = self.block_classes[path] = getattr(module, class_name)
|
||||||
|
|
||||||
|
return cls.construct_from_lookup(self, *args, **kwargs)
|
@ -17,6 +17,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from wagtail import blocks
|
from wagtail import blocks
|
||||||
from wagtail.blocks.base import get_error_json_data
|
from wagtail.blocks.base import get_error_json_data
|
||||||
|
from wagtail.blocks.definition_lookup import BlockDefinitionLookup
|
||||||
from wagtail.blocks.field_block import FieldBlockAdapter
|
from wagtail.blocks.field_block import FieldBlockAdapter
|
||||||
from wagtail.blocks.list_block import ListBlockAdapter, ListBlockValidationError
|
from wagtail.blocks.list_block import ListBlockAdapter, ListBlockValidationError
|
||||||
from wagtail.blocks.static_block import StaticBlockAdapter
|
from wagtail.blocks.static_block import StaticBlockAdapter
|
||||||
@ -5882,3 +5883,30 @@ class TestValidationErrorAsJsonData(TestCase):
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBlockDefinitionLookup(TestCase):
|
||||||
|
def test_get_block_definition(self):
|
||||||
|
lookup = BlockDefinitionLookup(
|
||||||
|
[
|
||||||
|
("wagtail.blocks.CharBlock", [], {"required": True}),
|
||||||
|
("wagtail.blocks.RichTextBlock", [], {}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
char_block = lookup.get_block(0)
|
||||||
|
char_block.set_name("title")
|
||||||
|
self.assertIsInstance(char_block, blocks.CharBlock)
|
||||||
|
self.assertTrue(char_block.required)
|
||||||
|
|
||||||
|
rich_text_block = lookup.get_block(1)
|
||||||
|
self.assertIsInstance(rich_text_block, blocks.RichTextBlock)
|
||||||
|
|
||||||
|
# A subsequent call to get_block with the same index should return a new instance;
|
||||||
|
# this ensures that state changes such as set_name are independent of other blocks
|
||||||
|
char_block_2 = lookup.get_block(0)
|
||||||
|
char_block_2.set_name("subtitle")
|
||||||
|
self.assertIsInstance(char_block, blocks.CharBlock)
|
||||||
|
self.assertTrue(char_block.required)
|
||||||
|
self.assertIsNot(char_block, char_block_2)
|
||||||
|
self.assertEqual(char_block.name, "title")
|
||||||
|
self.assertEqual(char_block_2.name, "subtitle")
|
||||||
|
Loading…
Reference in New Issue
Block a user