0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-12-01 11:41:20 +01:00

implement __setitem__ on StreamValue

This commit is contained in:
Matt Westcott 2020-10-21 02:02:41 +01:00
parent 603dc650e5
commit caade5f0de
2 changed files with 65 additions and 11 deletions

View File

@ -1,7 +1,7 @@
import uuid import uuid
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
from collections.abc import Sequence from collections.abc import MutableSequence
from django import forms from django import forms
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
@ -383,7 +383,7 @@ class StreamBlock(BaseStreamBlock, metaclass=DeclarativeSubBlocksMetaclass):
pass pass
class StreamValue(Sequence): class StreamValue(MutableSequence):
""" """
Custom type used to represent the value of a StreamBlock; behaves as a sequence of BoundBlocks Custom type used to represent the value of a StreamBlock; behaves as a sequence of BoundBlocks
(which keep track of block types in a way that the values alone wouldn't). (which keep track of block types in a way that the values alone wouldn't).
@ -446,16 +446,27 @@ class StreamValue(Sequence):
else: else:
# store native stream data in _bound_blocks; on serialization it will be converted to # store native stream data in _bound_blocks; on serialization it will be converted to
# a JSON-ish representation via block.get_prep_value. # a JSON-ish representation via block.get_prep_value.
self._bound_blocks = {} self._bound_blocks = {
for i, item in enumerate(stream_data): i: self._construct_stream_child(item)
try: for i, item in enumerate(stream_data)
type_name, value, block_id = item }
except ValueError:
type_name, value = item
block_id = None
block_def = self.stream_block.child_blocks[type_name] def _construct_stream_child(self, item):
self._bound_blocks[i] = StreamValue.StreamChild(block_def, value, id=block_id) """
Create a StreamChild instance from a (type, value, id) or (type, value) tuple,
or return item if it's already a StreamChild
"""
if isinstance(item, StreamValue.StreamChild):
return item
try:
type_name, value, block_id = item
except ValueError:
type_name, value = item
block_id = None
block_def = self.stream_block.child_blocks[type_name]
return StreamValue.StreamChild(block_def, value, id=block_id)
def __getitem__(self, i): def __getitem__(self, i):
if i not in range(0, self._length): if i not in range(0, self._length):
@ -467,6 +478,15 @@ class StreamValue(Sequence):
return self._bound_blocks[i] return self._bound_blocks[i]
def __setitem__(self, i, item):
self._bound_blocks[i] = self._construct_stream_child(item)
def __delitem__(self, i):
raise NotImplementedError
def insert(self, i, item):
raise NotImplementedError
def _prefetch_blocks(self, type_name): def _prefetch_blocks(self, type_name):
""" """
Populate _bound_blocks with all items in this stream of type `type_name` that exist in Populate _bound_blocks with all items in this stream of type `type_name` that exist in

View File

@ -3367,6 +3367,40 @@ class TestStreamBlock(WagtailTestUtils, SimpleTestCase):
{'type': 'paragraph', 'value': 'earth', 'id': '0002'}, {'type': 'paragraph', 'value': 'earth', 'id': '0002'},
]) ])
def test_set_streamvalue_item(self):
class ArticleBlock(blocks.StreamBlock):
heading = blocks.CharBlock()
paragraph = blocks.CharBlock()
block = ArticleBlock()
stream = block.to_python([
{'type': 'heading', 'value': 'hello', 'id': '0001'},
{'type': 'paragraph', 'value': 'world', 'id': '0002'},
])
stream[1] = ('heading', 'goodbye', '0003')
raw_data = block.get_prep_value(stream)
self.assertEqual(raw_data, [
{'type': 'heading', 'value': 'hello', 'id': '0001'},
{'type': 'heading', 'value': 'goodbye', 'id': '0003'},
])
@unittest.expectedFailure
def test_delete_streamvalue_item(self):
class ArticleBlock(blocks.StreamBlock):
heading = blocks.CharBlock()
paragraph = blocks.CharBlock()
block = ArticleBlock()
stream = block.to_python([
{'type': 'heading', 'value': 'hello', 'id': '0001'},
{'type': 'paragraph', 'value': 'world', 'id': '0002'},
])
del stream[0]
raw_data = block.get_prep_value(stream)
self.assertEqual(raw_data, [
{'type': 'paragraph', 'value': 'world', 'id': '0002'},
])
def test_render_with_classname_via_kwarg(self): def test_render_with_classname_via_kwarg(self):
"""form_classname from kwargs to be used as an additional class when rendering stream block""" """form_classname from kwargs to be used as an additional class when rendering stream block"""