How to display my selection box using WTForms?

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 &amp; 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) 
+3
source share
2 answers

EDIT:

If you want to always display the field with the options disabled, you need to create your own widget and field to provide the renderer.

In the current renderer, only three options are used: (value, name, selected) .

You will need to change this to accept the fourth optional element: disabled.

Based on the Select class in wtforms.widget:

 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)))) 

And then, based on the code in wtforms.fields, a subclass of SelectField that already exists

 class SelectFieldWithDisable(SelectFiel): widget = widgets.SelectWithDisable() def iter_choices(self): for value, label, selected, disabled in self.choices: yield (value, label, selected, disabled, self.coerce(value) == self.data) 

NOTE: THIS DOES NOT TEST DO NOT REPEAT THE PYTHON CODE, BUT VERY FAST HYIK HAS QUESTION AND CONTINUOUS CODE FROM WTFORMS. But he should give you enough of the initial beginning and the previous answer to completely control the field.

Use CSS and JavaScript to control the display element on the page.

In any template rendering system you use (I use a jar, jinja and wtforms) you render your elements and provide an id or class attribute when rendering it. (I just type form.select_field_variable_name )

Then create a CSS file to control the style and use JavaScript to control the custom disabling of certain elements, etc.

EDIT:

If you have:

 <select id=selector> <option id=value1 value=1>Bananas</option> <option id=value2 value=2>Corn</option> <option id=value3 value=3>Lolcats</option> </select> 

You can apply the background color with:

 <style> #selector {background-color: #beef99} </style> 

And you turn on / off with:

 <script> option = document.getElementById('value3') option.disabled = true </script> 

Etc etc. etc.

After you receive your element using the WTForms widget, like all HTML elements, you must create and manage any dynamic parts of the element using CSS and JavaScript

+4
source

Long after the fact, I went and figured out how to make the wtform part of @tkone's answer. I will add an answer to this question as it will not fit into the comment. Also, I tried to do this with SelectMultipleField , so my field class inherits from it instead of SelectField

First widget class:

 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' kwargs['size'] = len(field.choices) if len(field.choices) < 15 else 15 html = [u'<select %s>' % widgets.html_params(name=field.name, **kwargs)] for val, label, selected, disabled, coerced_value in field.iter_choices(): html.append(self.render_option(val, label, selected, disabled)) html.append(u'</select>') return widgets.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 widgets.HTMLString(u'<option %s>%s</option>' % (widgets.html_params(**options), escape(unicode(label)))) 

The only import change here is that I have from wtforms import widgets at the top of my .py forms, so I refer to widgets using widgets.HTMLString etc. I also added a size argument here, it might be better to be implemented somewhere else, which simply sets the element size to the number of elements or 15, whichever is smaller. I’m stuck with the fact that inside myself, to remind myself that I need to reconsider the size if I start using this widget in other ways.

Now the field class:

 class SelectMultipleFieldWithDisable(SelectMultipleField): widget = SelectWithDisable(multiple=True) def iter_choices(self): for value, label, selected, disabled in self.choices: yield (value, label, selected, disabled) 

All important changes have been made here. First, as mentioned earlier, the field is inherited from the SelectMultipleField class, so I add the multiple = True argument to the widget declaration. Finally, I remove the last element from the iter_choices ( self.coerce(value) == self.data ) self.coerce(value) == self.data . I'm not quite sure what this should have done, but in my case, he always compared an integer to a list and returned False and led to

ValueError: Too many values ​​to unpack

and

Requires more than x to unpack

An OP error was visible. If it returns something valuable, just add this extra variable to the for statement in the method call to the widget class.

Then, when I define the selection, I just need to set a selection tuple for each item that will be (value, label, selected, disabled) , where the boolean values ​​are selected and disabled, indicating whether the item should be selected and disabled accordingly.

I hope this helps someone get lost, as I was at one point on the line.

0
source

All Articles