I have a selection box in which some elements have disappeared and are disabled, which I would like to do using WTForms:
<select name="cg" id="cat" class="search_category"> <option value='' >{% trans %}All{% endtrans %}</option> <option value='' style='background-color:#dcdcc3' id='cat1' disabled="disabled">-- {% trans %}VEHICLES{% endtrans %} --</option> <option value='2' {% if "2" == cg %} selected="selected" {% endif %} id='cat2' >{% trans %}Cars{% endtrans %}</option> <option value='3' {% if "3" == cg %} selected="selected" {% endif %} id='cat3' >{% trans %}Motorcycles{% endtrans %}</option> <option value='4' {% if "4" == cg %} selected="selected" {% endif %} id='cat4' >{% trans %}Accessories & Parts{% endtrans %}</option> ...
I have a form class that works, and I started implementing it with a localized category variable, but I donβt know how to make a widget (?) That displays faded ( background-color:#dcdcc3 ) and disabled attributes in the option element
class AdForm(Form): my_choices = [('1', _('VEHICLES')), ('2', _('Cars')), ('3', _('Bicycles'))] name = TextField(_('Name'), [validators.Required(message=_('Name is required'))], widget=MyTextInput()) title = TextField(_('title'), [validators.Required(message=_('Subject is required'))], widget=MyTextInput()) text = TextAreaField(_('Text'),[validators.Required(message=_('Text is required'))], widget=MyTextArea()) phonenumber = TextField(_('Phone number')) phoneview = BooleanField(_('Display phone number on site')) price = TextField(_('Price'),[validators.Regexp('\d', message=_('This is not an integer number, please see the example and try again')),validators.Optional()] ) password = PasswordField(_('Password'),[validators.Optional()], widget=PasswordInput()) email = TextField(_('Email'), [validators.Required(message=_('Email is required')), validators.Email(message=_('Your email is invalid'))], widget=MyTextInput()) category = SelectField(choices = my_choices, default = '1') def validate_name(form, field): if len(field.data) > 50: raise ValidationError(_('Name must be less than 50 characters')) def validate_email(form, field): if len(field.data) > 60: raise ValidationError(_('Email must be less than 60 characters')) def validate_price(form, field): if len(field.data) > 8: raise ValidationError(_('Price must be less than 9 integers'))
I can use the category of variables at the top to display the selection for the categories. I also want to enable special rendering, i.e. disconnected items and faded background. Can you tell me how I should do this?
thanks
Update
When I try to resolve a disabled attribute from a response, I get this error message:
Trace: Traceback (most recent call last): File "/media/Lexar/montao/lib/webapp2/webapp2.py", line 545, in dispatch return method(*args, **kwargs) File "/media/Lexar/montao/montaoproject/i18n.py", line 438, in get current_user=self.current_user, File "/media/Lexar/montao/montaoproject/main.py", line 469, in render_jinja self.response.out.write(template.render(data)) File "/media/Lexar/montao/montaoproject/jinja2/environment.py", line 894, in render return self.environment.handle_exception(exc_info, True) File "/media/Lexar/montao/montaoproject/templates/insert_jinja.html", line 221, in top-level template code {{ form.category|safe }} ValueError: need more than 2 values to unpack
The code I tried was:
from wtforms.widgets import html_params class SelectWithDisable(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, disabled)`. """ def __init__(self, multiple=False): self.multiple = multiple def __call__(self, field, **kwargs): kwargs.setdefault('id', field.id) if self.multiple: kwargs['multiple'] = 'multiple' html = [u'<select %s>' % html_params(name=field.name, **kwargs)] for val, label, selected, disabled in field.iter_choices(): html.append(self.render_option(val, label, selected, disabled)) html.append(u'</select>') return HTMLString(u''.join(html)) @classmethod def render_option(cls, value, label, selected, disabled): options = {'value': value} if selected: options['selected'] = u'selected' if disabled: options['disabled'] = u'disabled' return HTMLString(u'<option %s>%s</option>' % (html_params(**options), escape(unicode(label)))) class SelectFieldWithDisable(SelectField): widget = SelectWithDisable() def iter_choices(self): for value, label, selected, disabled in self.choices: yield (value, label, selected, disabled, self.coerce(value) == self.data) class AdForm(Form): my_choices = [('1', _('VEHICLES')), ('2', _('Cars')), ('3', _('Motorcycles'))] nouser = HiddenField(_('No user')) name = TextField(_('Name'), [validators.Required(message=_('Name is required'))], widget=MyTextInput()) title = TextField(_('Subject'), [validators.Required(message=_('Subject is required'))], widget=MyTextInput()) text = TextAreaField(_('Text'),[validators.Required(message=_('Text is required'))], widget=MyTextArea()) phonenumber = TextField(_('Phone number')) phoneview = BooleanField(_('Display phone number on site')) price = TextField(_('Price'),[validators.Regexp('\d', message=_('This is not an integer number, please see the example and try again')),validators.Optional()] ) password = PasswordField(_('Password'),validators=[RequiredIf('nouser', message=_('Password is required'))], widget=MyPasswordInput()) email = TextField(_('Email'), [validators.Required(message=_('Email is required')), validators.Email(message=_('Your email is invalid'))], widget=MyTextInput()) category = SelectFieldWithDisable(choices = my_choices) def validate_name(form, field): if len(field.data) > 50: raise ValidationError(_('Name must be less than 50 characters')) def validate_email(form, field): if len(field.data) > 60: raise ValidationError(_('Email must be less than 60 characters')) def validate_price(form, field): if len(field.data) > 8: raise ValidationError(_('Price must be less than 9 integers'))
I suppose I should set the disabled attribute somewhere, but where?
Update 2
It was harder than I thought. The solution suggested on the wtforms mailing list was also proposed, but I could not get this to work (some trivial error about invalid syntax and was not able to import ecscape from wtforms, so the action I took updated my wtforms from the hg repository, if something important has changed there.
From the answer here, I either get Need more than 2 values to unpack , or ValueError: too many values to unpack , so I seem to understand that this is correct. In my template, what I'm trying to do is
{{ form.category }}
and my form class
class AdForm(Form): my_choices = [('1', _('VEHICLES'), False, True), ('2', _('Cars'), False, False), ('3', _('Motorcycles'), False, False)] ... category = SelectFieldWithDisable(choices = my_choices)
with classes added by me:
class SelectWithDisable(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, disabled)`. """ def __init__(self, multiple=False): self.multiple = multiple def __call__(self, field, **kwargs): kwargs.setdefault('id', field.id) if self.multiple: kwargs['multiple'] = 'multiple' html = [u'<select %s>' % html_params(name=field.name, **kwargs)] for val, label, selected, disabled in field.iter_choices(): html.append(self.render_option(val, label, selected, disabled)) html.append(u'</select>') return HTMLString(u''.join(html)) @classmethod def render_option(cls, value, label, selected, disabled): options = {'value': value} if selected: options['selected'] = u'selected' if disabled: options['disabled'] = u'disabled' return HTMLString(u'<option %s>%s</option>' % (html_params(**options), escape(unicode(label)))) class SelectFieldWithDisable(SelectField): widget = SelectWithDisable() def iter_choices(self): for value, label, selected, disabled in self.choices: yield (value, label, selected, disabled, self.coerce(value) == self.data)