1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
from copy import copy
from wtforms import SelectField as _SelectField
from wtforms.validators import ValidationError
from ..widgets import SelectWidget
class SelectField(_SelectField):
"""
Add support of ``optgroup``'s' to default WTForms' ``SelectField`` class.
So, next choices would be supported as well::
(
('Fruits', (
('apple', 'Apple'),
('peach', 'Peach'),
('pear', 'Pear')
)),
('Vegetables', (
('cucumber', 'Cucumber'),
('potato', 'Potato'),
('tomato', 'Tomato'),
))
)
Also supports lazy choices (callables that return an iterable)
"""
widget = SelectWidget()
def __init__(self, *args, **kwargs):
choices = kwargs.pop("choices", None)
if callable(choices):
super().__init__(*args, **kwargs)
self.choices = copy(choices)
else:
super().__init__(*args, choices=choices, **kwargs)
def iter_choices(self):
"""
We should update how choices are iter to make sure that value from
internal list or tuple should be selected.
"""
for value, label in self.concrete_choices:
yield (value, label, (self.coerce, self.data), {})
@property
def concrete_choices(self):
if callable(self.choices):
return self.choices()
return self.choices
@property
def choice_values(self):
values = []
for value, label in self.concrete_choices:
if isinstance(label, (list, tuple)):
for subvalue, sublabel in label:
values.append(subvalue)
else:
values.append(value)
return values
def pre_validate(self, form):
"""
Don't forget to validate also values from embedded lists.
"""
values = self.choice_values
if (self.data is None and "" in values) or self.data in values:
return True
raise ValidationError(self.gettext("Not a valid choice"))
|