diff options
author | tr4ck3ur <tr4ck3ur@style-python.fr> | 2015-02-13 02:29:37 +0100 |
---|---|---|
committer | tr4ck3ur <tr4ck3ur@style-python.fr> | 2015-02-13 02:29:37 +0100 |
commit | fa4a9859c57de6b7894ff4b84b75d242f2b796f5 (patch) | |
tree | c6ec352fdd634ca3e645cb2db897a127fcff299b /jm2l/ExtWtforms.py | |
download | jm2l-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.py | 163 |
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')) |