reshuffle rendering to go through the block's 'render' method (where subclasses can override it, and non-smart values can be given renderings too)

This commit is contained in:
Matt Westcott 2015-01-19 23:04:50 +00:00
parent 3aaebe3b6d
commit 5698a0368a

View file

@ -9,6 +9,7 @@ from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.deconstruct import deconstructible
from django.utils.functional import cached_property
from django.template.loader import render_to_string
from django.forms import Media
from django.forms.utils import ErrorList
@ -130,7 +131,7 @@ class Block(object):
def value_from_datadict(self, data, files, prefix):
raise NotImplementedError('%s.value_from_datadict' % self.__class__)
def bind(self, value, prefix, error=None):
def bind(self, value, prefix=None, error=None):
"""
Return a BoundBlock which represents the association of this block definition with a value
and a prefix (and optionally, a ValidationError to be rendered).
@ -138,7 +139,7 @@ class Block(object):
bound_block.render() rather than blockdef.render(value, prefix) which can't be called from
within a template.
"""
return BoundBlock(self, prefix, value, error=error)
return BoundBlock(self, value, prefix=prefix, error=error)
def prototype_block(self):
"""
@ -178,17 +179,26 @@ class Block(object):
"""
return value
def render(self, value):
"""
Return a text rendering of 'value', suitable for display on templates.
"""
return force_text(value)
class BoundBlock(object):
def __init__(self, block, prefix, value, error=None):
def __init__(self, block, value, prefix=None, error=None):
self.block = block
self.prefix = prefix
self.value = value
self.prefix = prefix
self.error = error
def render_form(self):
return self.block.render_form(self.value, self.prefix, error=self.error)
def render(self):
return self.block.render(self.value)
# ==========
# Text input
@ -381,6 +391,9 @@ class BaseStructBlock(Block):
for name, val in value.items()
])
def render(self, value):
return render_to_string(self.template, {'self': value})
@python_2_unicode_compatible # ensures that the output of __str__ doesn't lose its 'safe' flag
class RenderableStructBlock(dict):
def __init__(self, block, *args):
@ -388,7 +401,7 @@ class RenderableStructBlock(dict):
self.block = block
def __str__(self):
return render_to_string(self.block.template, {'self': self})
return self.block.render(self)
class DeclarativeSubBlocksMetaclass(type):
@ -654,7 +667,7 @@ class BaseStreamBlock(Block):
)
values_with_indexes.sort()
return StreamValue([(val, typ) for (index, val, typ) in values_with_indexes])
return StreamValue(self, [(val, typ) for (index, val, typ) in values_with_indexes])
def clean(self, value):
result = []
@ -675,22 +688,28 @@ class BaseStreamBlock(Block):
# which only involves the 'params' list
raise ValidationError('Validation error in StreamBlock', params=errors)
return StreamValue(result)
return StreamValue(self, result)
def to_python(self, value):
# the incoming JSONish representation is a list of dicts, each with a 'type' and 'value' field.
# Convert this to a StreamValue backed by a list of (value, type) tuples
return StreamValue([
return StreamValue(self, [
(self.child_blocks[item['type']].to_python(item['value']), item['type'])
for item in value
])
def get_prep_value(self, value):
return [
{'type': block_type, 'value': self.child_blocks[block_type].get_prep_value(child_value)}
for child_value, block_type in value.values_with_types
{'type': bound_block.block.name, 'value': bound_block.block.get_prep_value(bound_block.value)}
for bound_block in value.bound_blocks
]
def render(self, value):
return format_html_join('\n', '<div class="block-{1}">{0}</div>',
[(bound_block.render(), bound_block.block.name) for bound_block in value.bound_blocks]
)
class StreamBlock(six.with_metaclass(DeclarativeSubBlocksMetaclass, BaseStreamBlock)):
pass
@ -702,7 +721,8 @@ class StreamValue(collections.Sequence):
so that we can naturally iterate over it in template code, but also allows retrieval of
(value, type) tuples as required for the form (and other complex rendering logic) to work.
"""
def __init__(self, values_with_types):
def __init__(self, stream_block, values_with_types):
self.stream_block = stream_block
self.values_with_types = values_with_types
def __getitem__(self, i):
@ -715,6 +735,11 @@ class StreamValue(collections.Sequence):
return repr(list(self))
def __str__(self):
return format_html_join('\n', '<div class="block-{1}">{0}</div>',
[(force_text(value), block_type) for value, block_type in self.values_with_types]
)
return self.stream_block.render(self)
@cached_property
def bound_blocks(self):
return [
BoundBlock(self.stream_block.child_blocks[block_type], value)
for value, block_type in self.values_with_types
]