aboutsummaryrefslogtreecommitdiffstats
path: root/jm2l/ExtWtforms.py
diff options
context:
space:
mode:
authortr4ck3ur <tr4ck3ur@style-python.fr>2015-02-13 02:29:37 +0100
committertr4ck3ur <tr4ck3ur@style-python.fr>2015-02-13 02:29:37 +0100
commitfa4a9859c57de6b7894ff4b84b75d242f2b796f5 (patch)
treec6ec352fdd634ca3e645cb2db897a127fcff299b /jm2l/ExtWtforms.py
downloadjm2l-fa4a9859c57de6b7894ff4b84b75d242f2b796f5.tar.gz
jm2l-fa4a9859c57de6b7894ff4b84b75d242f2b796f5.tar.bz2
jm2l-fa4a9859c57de6b7894ff4b84b75d242f2b796f5.tar.xz
jm2l-fa4a9859c57de6b7894ff4b84b75d242f2b796f5.zip
first drop
Diffstat (limited to 'jm2l/ExtWtforms.py')
-rw-r--r--jm2l/ExtWtforms.py163
1 files changed, 163 insertions, 0 deletions
diff --git a/jm2l/ExtWtforms.py b/jm2l/ExtWtforms.py
new file mode 100644
index 0000000..e84418a
--- /dev/null
+++ b/jm2l/ExtWtforms.py
@@ -0,0 +1,163 @@
+try:
+ from html import escape
+except ImportError:
+ from cgi import escape
+
+#from wtforms import widgets
+from wtforms.widgets import HTMLString, html_params
+from wtforms.fields.core import Field
+from wtforms.compat import text_type, izip
+
+class MySelect(object):
+ """
+ Renders a select field.
+
+ If `multiple` is True, then the `size` property should be specified on
+ rendering to make the field useful.
+
+ The field must provide an `iter_choices()` method which the widget will
+ call on rendering; this method must yield tuples of
+ `(value, label, selected)`.
+ """
+ def __init__(self, multiple=False):
+ self.multiple = multiple
+
+ def __call__(self, field, **kwargs):
+ kwargs.setdefault('id', field.id)
+ if self.multiple:
+ kwargs['multiple'] = True
+ html = ['<select %s>' % html_params(name=field.name, **kwargs)]
+ last_group = None
+ for group, val, label, selected in field.iter_choices():
+ if group is None:
+ html.append(self.render_option(val, label, selected))
+ elif last_group != group:
+ html.append(self.render_optgroup(last_group, group))
+ html.append(self.render_option(val, label, selected))
+ last_group=group
+ else:
+ html.append(self.render_option(val, label, selected))
+ if last_group:
+ html.append(self.render_optgroup(last_group, None))
+ html.append('</select>')
+ return HTMLString(''.join(html))
+
+ @classmethod
+ def render_option(cls, value, label, selected, **kwargs):
+ if value is True:
+ # Handle the special case of a 'True' value.
+ value = text_type(value)
+
+ options = dict(kwargs, value=value)
+ if selected:
+ options['selected'] = True
+ return HTMLString('<option %s>%s</option>' % (html_params(**options), escape(text_type(label), quote=False)))
+
+ @classmethod
+ def render_optgroup(cls, previous_label, label, **kwargs):
+ options = dict(kwargs)
+ if previous_label is None:
+ return HTMLString('<optgroup %s label="%s">' % (html_params(**options), escape(text_type(label), quote=False)))
+ elif label is None:
+ return HTMLString('</optgroup>')
+ else:
+ return HTMLString('</optgroup><optgroup %s label="%s">' % (html_params(**options), escape(text_type(label), quote=False)))
+
+
+class MyOption(object):
+ """
+ Renders the individual option from a select field.
+
+ This is just a convenience for various custom rendering situations, and an
+ option by itself does not constitute an entire field.
+ """
+ def __call__(self, field, **kwargs):
+ return MySelect.render_option(field._value(), field.label.text, field.checked, **kwargs)
+
+
+class MySelectFieldBase(Field):
+ #option_widget = widgets.Option()
+ option_widget = MyOption()
+
+ """
+ Base class for fields which can be iterated to produce options.
+
+ This isn't a field, but an abstract base class for fields which want to
+ provide this functionality.
+ """
+ def __init__(self, label=None, validators=None, option_widget=None, **kwargs):
+ super(MySelectFieldBase, self).__init__(label, validators, **kwargs)
+
+ if option_widget is not None:
+ self.option_widget = option_widget
+
+ def iter_choices(self):
+ """
+ Provides data for choice widget rendering. Must return a sequence or
+ iterable of (value, label, selected) tuples.
+ """
+ raise NotImplementedError()
+
+ def __iter__(self):
+ opts = dict(widget=self.option_widget, _name=self.name, _form=None, _meta=self.meta)
+ for i, (value, label, checked) in enumerate(self.iter_choices()):
+ opt = self._Option(label=label, id='%s-%d' % (self.id, i), **opts)
+ opt.process(None, value)
+ opt.checked = checked
+ yield opt
+
+ class _Option(Field):
+ checked = False
+
+ def _value(self):
+ return text_type(self.data)
+
+
+class MySelectField(MySelectFieldBase):
+ #widget = widgets.Select()
+ widget = MySelect()
+
+ def __init__(self, label=None, validators=None, coerce=text_type, choices=None, **kwargs):
+ super(MySelectField, self).__init__(label, validators, **kwargs)
+ self.coerce = coerce
+ self.choices = choices
+
+ def iter_choices(self):
+ for choiceA, choiceB in self.choices:
+ if isinstance(choiceB, (tuple, list)):
+ # We should consider choiceA as an optgroup label
+ group_label = choiceA
+ for value, label in choiceB:
+ yield (group_label, value, label, self.coerce(value) == self.data)
+ else:
+ value, label = choiceA, choiceB
+ # Not an optgroup, let's fallback to classic usage
+ yield (None, value, label, self.coerce(value) == self.data)
+
+ def process_data(self, value):
+ try:
+ self.data = self.coerce(value)
+ except (ValueError, TypeError):
+ self.data = None
+
+ def process_formdata(self, valuelist):
+ if valuelist:
+ try:
+ self.data = self.coerce(valuelist[0])
+ except ValueError:
+ raise ValueError(self.gettext('Invalid Choice: could not coerce'))
+
+ def pre_validate(self, form):
+ for choiceA, choiceB in self.choices:
+ if isinstance(choiceB, (tuple, list)):
+ for value, label in choiceB:
+ if self.data == value:
+ break
+ if self.data == value:
+ break
+ else:
+ value, label = choiceA, choiceB
+ if self.data == value:
+ break
+ else:
+ raise ValueError(self.gettext('Not a valid choice'))