move ListBlock into wagtailcore.blocks.list_block

This commit is contained in:
Matt Westcott 2015-02-19 19:18:22 +00:00
parent ad76273b54
commit 5936326726
2 changed files with 157 additions and 144 deletions

View file

@ -18,8 +18,6 @@ from django.forms.utils import ErrorList
import six
from wagtail.wagtailcore.utils import escape_script
from .utils import js_dict
@ -506,148 +504,6 @@ class StructBlock(six.with_metaclass(DeclarativeSubBlocksMetaclass, BaseStructBl
pass
# =========
# ListBlock
# =========
class ListBlock(Block):
class Meta:
# Default to a list consisting of one empty child item (using None to trigger the child's empty / default rendering)
default = [None]
def __init__(self, child_block, **kwargs):
super(ListBlock, self).__init__(**kwargs)
if isinstance(child_block, type):
# child_block was passed as a class, so convert it to a block instance
self.child_block = child_block()
else:
self.child_block = child_block
self.dependencies = [self.child_block]
self.child_js_initializer = self.child_block.js_initializer()
@property
def media(self):
return forms.Media(js=['wagtailadmin/js/blocks/sequence.js', 'wagtailadmin/js/blocks/list.js'])
def render_list_member(self, value, prefix, index, errors=None):
"""
Render the HTML for a single list item in the form. This consists of an <li> wrapper, hidden fields
to manage ID/deleted state, delete/reorder buttons, and the child block's own form HTML.
"""
child = self.child_block.bind(value, prefix="%s-value" % prefix, errors=errors)
return render_to_string('wagtailadmin/block_forms/list_member.html', {
'prefix': prefix,
'child': child,
'index': index,
})
def html_declarations(self):
# generate the HTML to be used when adding a new item to the list;
# this is the output of render_list_member as rendered with the prefix '__PREFIX__'
# (to be replaced dynamically when adding the new item) and the child block's default value
# as its value.
list_member_html = self.render_list_member(self.child_block.meta.default, '__PREFIX__', '')
return format_html(
'<script type="text/template" id="{0}-newmember">{1}</script>',
self.definition_prefix, mark_safe(escape_script(list_member_html))
)
def js_initializer(self):
opts = {'definitionPrefix': "'%s'" % self.definition_prefix}
if self.child_js_initializer:
opts['childInitializer'] = self.child_js_initializer
return "ListBlock(%s)" % js_dict(opts)
def render_form(self, value, prefix='', errors=None):
if errors:
if len(errors) > 1:
# We rely on ListBlock.clean throwing a single ValidationError with a specially crafted
# 'params' attribute that we can pull apart and distribute to the child blocks
raise TypeError('ListBlock.render_form unexpectedly received multiple errors')
error_list = errors.as_data()[0].params
else:
error_list = None
list_members_html = [
self.render_list_member(child_val, "%s-%d" % (prefix, i), i,
errors=error_list[i] if error_list else None)
for (i, child_val) in enumerate(value)
]
return render_to_string('wagtailadmin/block_forms/list.html', {
'label': self.label,
'prefix': prefix,
'list_members_html': list_members_html,
})
def value_from_datadict(self, data, files, prefix):
count = int(data['%s-count' % prefix])
values_with_indexes = []
for i in range(0, count):
if data['%s-%d-deleted' % (prefix, i)]:
continue
values_with_indexes.append(
(
data['%s-%d-order' % (prefix, i)],
self.child_block.value_from_datadict(data, files, '%s-%d-value' % (prefix, i))
)
)
values_with_indexes.sort()
return [v for (i, v) in values_with_indexes]
def clean(self, value):
result = []
errors = []
for child_val in value:
try:
result.append(self.child_block.clean(child_val))
except ValidationError as e:
errors.append(ErrorList([e]))
else:
errors.append(None)
if any(errors):
# The message here is arbitrary - outputting error messages is delegated to the child blocks,
# which only involves the 'params' list
raise ValidationError('Validation error in ListBlock', params=errors)
return result
def to_python(self, value):
# recursively call to_python on children and return as a list
return [
self.child_block.to_python(item)
for item in value
]
def get_prep_value(self, value):
# recursively call get_prep_value on children and return as a list
return [
self.child_block.get_prep_value(item)
for item in value
]
def render_basic(self, value):
children = format_html_join('\n', '<li>{0}</li>',
[(self.child_block.render(child_value),) for child_value in value]
)
return format_html("<ul>{0}</ul>", children)
def get_searchable_content(self, value):
content = []
for child_value in value:
content.extend(self.child_block.get_searchable_content(child_value))
return content
# ========================
# django.forms integration
# ========================
@ -703,4 +559,5 @@ class BlockField(forms.Field):
# Import block types defined in submodules into the wagtail.wagtailcore.blocks namespace
from .field import * # NOQA
from .list_block import * # NOQA
from .stream import * # NOQA

View file

@ -0,0 +1,156 @@
from __future__ import absolute_import, unicode_literals
from django import forms
from django.core.exceptions import ValidationError
from django.forms.utils import ErrorList
from django.template.loader import render_to_string
from django.utils.html import format_html, format_html_join
from django.utils.safestring import mark_safe
from wagtail.wagtailcore.blocks import Block
from wagtail.wagtailcore.utils import escape_script
from .utils import js_dict
__all__ = ['ListBlock']
class ListBlock(Block):
class Meta:
# Default to a list consisting of one empty child item (using None to trigger the child's empty / default rendering)
default = [None]
def __init__(self, child_block, **kwargs):
super(ListBlock, self).__init__(**kwargs)
if isinstance(child_block, type):
# child_block was passed as a class, so convert it to a block instance
self.child_block = child_block()
else:
self.child_block = child_block
self.dependencies = [self.child_block]
self.child_js_initializer = self.child_block.js_initializer()
@property
def media(self):
return forms.Media(js=['wagtailadmin/js/blocks/sequence.js', 'wagtailadmin/js/blocks/list.js'])
def render_list_member(self, value, prefix, index, errors=None):
"""
Render the HTML for a single list item in the form. This consists of an <li> wrapper, hidden fields
to manage ID/deleted state, delete/reorder buttons, and the child block's own form HTML.
"""
child = self.child_block.bind(value, prefix="%s-value" % prefix, errors=errors)
return render_to_string('wagtailadmin/block_forms/list_member.html', {
'prefix': prefix,
'child': child,
'index': index,
})
def html_declarations(self):
# generate the HTML to be used when adding a new item to the list;
# this is the output of render_list_member as rendered with the prefix '__PREFIX__'
# (to be replaced dynamically when adding the new item) and the child block's default value
# as its value.
list_member_html = self.render_list_member(self.child_block.meta.default, '__PREFIX__', '')
return format_html(
'<script type="text/template" id="{0}-newmember">{1}</script>',
self.definition_prefix, mark_safe(escape_script(list_member_html))
)
def js_initializer(self):
opts = {'definitionPrefix': "'%s'" % self.definition_prefix}
if self.child_js_initializer:
opts['childInitializer'] = self.child_js_initializer
return "ListBlock(%s)" % js_dict(opts)
def render_form(self, value, prefix='', errors=None):
if errors:
if len(errors) > 1:
# We rely on ListBlock.clean throwing a single ValidationError with a specially crafted
# 'params' attribute that we can pull apart and distribute to the child blocks
raise TypeError('ListBlock.render_form unexpectedly received multiple errors')
error_list = errors.as_data()[0].params
else:
error_list = None
list_members_html = [
self.render_list_member(child_val, "%s-%d" % (prefix, i), i,
errors=error_list[i] if error_list else None)
for (i, child_val) in enumerate(value)
]
return render_to_string('wagtailadmin/block_forms/list.html', {
'label': self.label,
'prefix': prefix,
'list_members_html': list_members_html,
})
def value_from_datadict(self, data, files, prefix):
count = int(data['%s-count' % prefix])
values_with_indexes = []
for i in range(0, count):
if data['%s-%d-deleted' % (prefix, i)]:
continue
values_with_indexes.append(
(
data['%s-%d-order' % (prefix, i)],
self.child_block.value_from_datadict(data, files, '%s-%d-value' % (prefix, i))
)
)
values_with_indexes.sort()
return [v for (i, v) in values_with_indexes]
def clean(self, value):
result = []
errors = []
for child_val in value:
try:
result.append(self.child_block.clean(child_val))
except ValidationError as e:
errors.append(ErrorList([e]))
else:
errors.append(None)
if any(errors):
# The message here is arbitrary - outputting error messages is delegated to the child blocks,
# which only involves the 'params' list
raise ValidationError('Validation error in ListBlock', params=errors)
return result
def to_python(self, value):
# recursively call to_python on children and return as a list
return [
self.child_block.to_python(item)
for item in value
]
def get_prep_value(self, value):
# recursively call get_prep_value on children and return as a list
return [
self.child_block.get_prep_value(item)
for item in value
]
def render_basic(self, value):
children = format_html_join('\n', '<li>{0}</li>',
[(self.child_block.render(child_value),) for child_value in value]
)
return format_html("<ul>{0}</ul>", children)
def get_searchable_content(self, value):
content = []
for child_value in value:
content.extend(self.child_block.get_searchable_content(child_value))
return content
DECONSTRUCT_ALIASES = {
ListBlock: 'wagtail.wagtailcore.blocks.ListBlock',
}