Call Field.prepare_value from FieldBlocks

This is used by some form fields to convert from the internal Python
value to the value used by a widget, much like `Block.get_prep_value`
for blocks or `Field.get_prep_value` for model fields.
pull/3679/head
Tim Heap 2017-06-21 12:08:53 +10:00 zatwierdzone przez Matt Westcott
rodzic 0965ea743b
commit 55a56416f5
4 zmienionych plików z 62 dodań i 3 usunięć

Wyświetl plik

@ -4,6 +4,8 @@ Changelog
1.12 (xx.xx.xxxx) - IN DEVELOPMENT
~~~~~~~~~~~~~~~~~
* Fix: FieldBlocks in StreamField now call the field's `prepare_value` method (Tim Heap)
1.11 (xx.xx.xxxx) - IN DEVELOPMENT
~~~~~~~~~~~~~~~~~

Wyświetl plik

@ -16,6 +16,8 @@ Other features
Bug fixes
~~~~~~~~~
* FieldBlocks in StreamField now call the field's ``prepare_value`` method (Tim Heap)
Upgrade considerations
======================

Wyświetl plik

@ -26,11 +26,12 @@ class FieldBlock(Block):
return self.field.widget.id_for_label(prefix)
def render_form(self, value, prefix='', errors=None):
widget = self.field.widget
field = self.field
widget = field.widget
widget_attrs = {'id': prefix, 'placeholder': self.label}
field_value = self.value_for_form(value)
field_value = field.prepare_value(self.value_for_form(value))
if hasattr(widget, 'render_with_errors'):
widget_html = widget.render_with_errors(prefix, field_value, attrs=widget_attrs, errors=errors)
@ -43,7 +44,7 @@ class FieldBlock(Block):
'name': self.name,
'classes': self.meta.classname,
'widget': widget_html,
'field': self.field,
'field': field,
'errors': errors if (not widget_has_rendered_errors) else None
})

Wyświetl plik

@ -176,6 +176,60 @@ class TestFieldBlock(WagtailTestUtils, SimpleTestCase):
self.assertIn('pretty.css', ''.join(block.all_media().render_css()))
self.assertIn('animations.js', ''.join(block.all_media().render_js()))
def test_prepare_value_called(self):
"""
Check that Field.prepare_value is called before sending the value to
the widget for rendering.
Actual real-world use case: A Youtube field that produces YoutubeVideo
instances from IDs, but videos are entered using their full URLs.
"""
class PrefixWrapper(object):
prefix = 'http://example.com/'
def __init__(self, value):
self.value = value
def with_prefix(self):
return self.prefix + self.value
@classmethod
def from_prefixed(cls, value):
if not value.startswith(cls.prefix):
raise ValueError
return cls(value[len(cls.prefix):])
def __eq__(self, other):
return self.value == other.value
class PrefixField(forms.Field):
def clean(self, value):
value = super(PrefixField, self).clean(value)
return PrefixWrapper.from_prefixed(value)
def prepare_value(self, value):
return value.with_prefix()
class PrefixedBlock(blocks.FieldBlock):
def __init__(self, required=True, help_text='', **kwargs):
super(PrefixedBlock, self).__init__(**kwargs)
self.field = PrefixField(required=required, help_text=help_text)
block = PrefixedBlock()
# Check that the form value is serialized with a prefix correctly
value = PrefixWrapper('foo')
html = block.render_form(value, 'url')
self.assertInHTML(
'<input id="url" name="url" placeholder="" type="text" value="{}" />'.format(
value.with_prefix()),
html)
# Check that the value was coerced back to a PrefixValue
data = {'url': 'http://example.com/bar'}
new_value = block.clean(block.value_from_datadict(data, {}, 'url'))
self.assertEqual(new_value, PrefixWrapper('bar'))
class TestIntegerBlock(unittest.TestCase):
def test_type(self):