Forms¶
ToscaWidgets provides all the widgets related to
building HTML Forms in the tw2.forms
package.
While tw2.core
implements the foundation for
declaring any kind of widget, the tw2.forms
is specialised in widgets that are needed to
create HTML forms.
Form¶
A form is usually created by declaring a subclass
of a tw2.forms.Form
. Within the
form a child
attribute that specifies the Form Layout
(how the fields shoulb be arranged graphically)
through a subclass of tw2.forms.Layout
and then
within child
all the fields of the form can be declared:
import tw2.core as twc
import tw2.forms as twf
class MovieForm(twf.Form):
class child(twf.TableLayout):
title = twf.TextField()
director = twf.TextField(value='Default Director')
genres = twf.SingleSelectField(options=['Action', 'Comedy', 'Romance', 'Sci-fi'])
action = '/save_movie'
The form must also provide an action
attribute
to specify where the form should be submitted.
Note
If you are going to use ToscaWidgets with TurboGears
you probably want the action to be a tg.lurl
to
ensure that prefix of your application is retained.
Form Buttons¶
By default, each form comes with a submit button.
The submit button can be replaced by setting the
form submit
attribute:
class NameForm(twf.Form):
class child(twf.TableLayout):
name = twf.TextField()
action = '/save_name'
submit = twf.SubmitButton(value="Save Name")
Multiple buttons can also be provided for the form
by setting the buttons
attribute:
class NameForm(twf.Form):
class child(twf.TableLayout):
name = twf.TextField()
action = '/save_name'
buttons = [
twf.SubmitButton(value="Save Name"),
twf.ResetButton(),
twf.Button(value="Say Hi", attrs=dict(onclick="alert('hi')"))
]
Dynamic Forms¶
Children can be added and removed dynamically from forms
using the Widget.post_define()
and Widget.prepare()
methods.
For example to change children of a form based on an option,
Widget.post_define()
can be used:
class GrowingMovieForm(twf.Form):
class child(twf.TableLayout):
@classmethod
def post_define(cls):
if not cls.parent:
return
children = []
for count in range(cls.parent.num_contacts):
class person_fieldset(twf.TableFieldSet):
id = "person_%s" % count
label = "Person #%s" % count
name = twf.TextField(validator=twc.Validator(required=True))
surname = twf.TextField()
children.append(person_fieldset(parent=cls))
cls.children = children
action = '/save_contacts'
num_contacts = twc.Param(default=1)
fivefieldsform = GrowingMovieForm(num_contacts=5)
Note
Use the same fivefieldsform
object for both display and
validation. Trying to make a new GrowingMovieForm
might not work
even though num_contacts
is always set to 5
.
This will not work btw if you need to take action at display time.
In such case Widget.prepare()
is needed, for example to have
a text field that suggests the placeholder based on its original value:
class DynamicText(twf.Form):
class child(twf.TableLayout):
text = twf.TextField(placeholder="Put text here")
action = "/save_movie"
def prepare(self):
super(DynamicText, self).prepare()
if self.child.children.text.value:
self.child.children.text.attrs = dict(
self.child.children.text.attrs,
placeholder="Put text here (was %s)" % self.child.children.text.value
)
Note
Widget.prepare()
is usually involved when setting a state that
depends on the current request. For example current value of a field,
or something else that is known only in current request. The resulting
state of the widget is also only valid in current request, a different
request might have nothing in common. Keep this in mind when using
validation, as validation usually happens in a different request from
the one that displayed the widget.
-
class
tw2.forms.widgets.
Form
(**kw)[source]¶ A form, with a submit button. It’s common to pass a TableLayout or ListLayout widget as the child.
-
classmethod
post_define
()[source]¶ This is a class method, that is called when a subclass of this Widget is created. Process static configuration here. Use it like this:
class MyWidget(LeafWidget): @classmethod def post_define(cls): id = getattr(cls, 'id', None) if id and not id.startswith('my'): raise pm.ParameterError("id must start with 'my'")
post_define should always cope with missing data - the class may be an abstract class. There is no need to call super(), the metaclass will do this automatically.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
submit
¶ alias of
tw2.core.params.SubmitButton_s_s
-
classmethod
Validating Forms¶
When you submit a form, it will send its data to the endpoint
you specified through the action
parameter.
Before using it, you probably want to make sure that the data that was sent is correct and display back to the user error messages when it is not.
This can be done through Validation and thanks to the fact that Forms remember which form was just validated in the current request.
For each field in the form it is possible to specify
a validator=
parameter, which will be in charge of
validation for that field:
class ValidatedForm(twf.Form):
class child(twf.TableLayout):
number = twf.TextField(placeholder="a number (1, 2, 3, 4)",
validator=twc.validation.IntValidator())
required = twf.TextField(validator=twc.Required)
To validate the data submitted through this form you can use
the tw2.forms.widgets.Form.validate()
method.
If the validation passes, the method will return the validated data:
>>> ValidatedForm.validate({'numer': 5, 'required': 'hello'})
{'numer': 5, 'required': 'hello'}
If the validation fails, it will raise a tw2.core.validation.ValidationError
exception:
Traceback (most recent call last):
File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 106, in wrapper
d = fn(self, *args, **kw)
File "/home/amol/wrk/tw2.core/tw2/core/widgets.py", line 718, in _validate
raise vd.ValidationError('childerror', exception_validator)
tw2.core.validation.ValidationError
Such error can be trapped to get back the validated widget, the value that was being validated and the error message for each of its children:
>>> try:
... ValidatedForm.validate({'numer': 'Hello', 'required': ''})
... except tw2.core.validation.ValidationError as e:
... print(e.widget.child.value)
... for c in e.widget.child.children:
... print(c.compound_key, ':', c.error_msg)
{'numer': 'Hello', 'required': ''}
numer : Must be an integer
required : Enter a value
Also, trying to display back the form that was just validated, will print out the error message for each field:
>>> try:
... ValidatedForm.validate({'numer': 'Hello', 'required': ''})
... except tw2.core.validation.ValidationError as e:
... print(e.widget.display())
<form enctype="multipart/form-data" method="post">
<span class="error"></span>
<table>
<tr class="odd error" id="numer:container">
<th><label for="numer">Numer</label></th>
<td>
<input id="numer" name="numer" placeholder="a number (1, 2, 3, 4)" type="text" value="Hello"/>
<span id="numer:error">Must be an integer</span>
</td>
</tr><tr class="even required error" id="required:container">
<th><label for="required">Required</label></th>
<td>
<input id="required" name="required" type="text" value=""/>
<span id="required:error">Enter a value</span>
</td>
</tr>
</table>
<input type="submit" value="Save"/>
</form>
For convenience, you can also recover the currently validated instance of the form anywhere in the code. Even far away from the exception that reported the validation error.
This can be helpful when you are isolating validation into a separate Aspect of your application and then you need to recover the form instance that includes the errors to display into your views.
To retrieve the currently validated widget, you can just use tw2.core.widget.Widget.req()
:
>>> try:
... ValidatedForm.validate({'numer': 'Hello', 'required': ''})
... except tw2.core.validation.ValidationError as e:
... print(e.widget)
... print(ValidatedForm.req())
<__main__.ValidatedForm object at 0x7f9432e5e080>
<__main__.ValidatedForm object at 0x7f9432e5e080>
As you can see ValidatedForm.req()
returns the same exact instance
that e.widget
was. That’s because when Widget.req()
is used
and there is a validated instance of that same exact widget in the current
request, ToscaWidgets will assume you are trying to access the widget
you just validated and will return that one instace of building a
new instance.
If you want a new instance, you can still do ValidatedForm().req()
instead of ValidatedForm.req()
:
>>> try:
... ValidatedForm.validate({'numer': 'Hello', 'required': ''})
... except tw2.core.validation.ValidationError as e:
... print(e.widget)
... print(ValidatedForm().req())
<__main__.ValidatedForm object at 0x7f9432e5e080>
<tw2.core.params.ValidatedForm_d object at 0x7f9432420940>
Keep in mind that this only keeps memory of the last widget
that failed validation. So in case multiple widgets failed validation
in the same request, you must used tw2.core.validation.ValidationError.widget
to access each one of them.
Form Layout¶
A layout specifies how the fields of the form should be arranged.
This can be specified by having Form.child
inherit from a specific layout class:
class NameForm(twf.Form):
class child(twf.TableLayout):
name = twf.TextField()
or:
class NameForm(twf.Form):
class child(twf.ListLayout):
name = twf.TextField()
Custom Layouts¶
A custom layout class can also be made to show the children however you want:
class Bootstrap3Layout(twf.BaseLayout):
inline_engine_name = "kajiki"
template = """
<div py:attrs="w.attrs">
<div class="form-group" py:for="c in w.children_non_hidden" title="${w.hover_help and c.help_text or None}" py:attrs="c.container_attrs" id="${c.compound_id}:container">
<label for="${c.id}" py:if="c.label">$c.label</label>
${c.display(attrs={"class": "form-control"})}
<span id="${c.compound_id}:error" class="error help-block" py:content="c.error_msg"/>
</div>
<py:for each="c in w.children_hidden">${c.display()}</py:for>
<div id="${w.compound_id}:error" py:content="w.error_msg"></div>
</div>"""
class BootstrapNameForm(twf.Form):
class child(Bootstrap3Layout):
name = twf.TextField()
submit = twf.SubmitButton(css_class="btn btn-default")
Complex Layouts¶
In case of complex custom layouts, you can even
specify the layout case by case in the form itself
with each children in a specific position accessing
the children using w.children.child_name
:
class OddNameForm(twf.Form):
class child(twf.BaseLayout):
inline_engine_name = "kajiki"
template = """
<div py:attrs="w.attrs">
<div py:with="c=w.children.name">
${c.display()}
<span id="${c.compound_id}:error" py:content="c.error_msg"/>
</div>
<div py:with="c=w.children.surname">
${c.display()}
<span id="${c.compound_id}:error" py:content="c.error_msg"/>
</div>
<py:for each="ch in w.children_hidden">${ch.display()}</py:for>
<div id="${w.compound_id}:error" py:content="w.error_msg"></div>
</div>
"""
name = twf.TextField()
surname = twf.TextField()
-
class
tw2.forms.widgets.
BaseLayout
(**kw)[source]¶ The following CSS classes are used, on the element containing both a child widget and its label.
- odd / even
- On alternating rows. The first row is odd.
- required
- If the field is a required field.
- error
- If the field contains a validation error.
-
class
tw2.forms.widgets.
ListLayout
(**kw)[source]¶ Arrange widgets and labels in a list.
The following CSS classes are used, on the element containing both a child widget and its label.
- odd / even
- On alternating rows. The first row is odd.
- required
- If the field is a required field.
- error
- If the field contains a validation error.
-
class
tw2.forms.widgets.
TableLayout
(**kw)[source]¶ Arrange widgets and labels in a table.
The following CSS classes are used, on the element containing both a child widget and its label.
- odd / even
- On alternating rows. The first row is odd.
- required
- If the field is a required field.
- error
- If the field contains a validation error.
Bultin Form Fields¶
tw2.forms
package comes with a bunch of builtin
widgets that can help you build the most common kind
of forms.
-
class
tw2.forms.widgets.
FormField
(**kw)[source]¶ Basic Form Widget from which each other field will inherit
-
name
¶ Name of the field
-
required
¶ If the field is required according to its validator (read-only)
-
-
class
tw2.forms.widgets.
TextFieldMixin
(**kw)[source]¶ Misc mixin class with attributes for textual input fields
-
maxlength
= None¶ Maximum length of the field
-
placeholder
= None¶ Placeholder text, until user writes something.
-
-
class
tw2.forms.widgets.
InputField
(**kw)[source]¶ A generic <input> field.
Generally you won’t use this one, but will rely on one of its specialised subclasses like
TextField
orCheckbox
.-
type
= None¶ Input type
-
value
= None¶ Current value of the input
-
required
= None¶ Add required attributed to the input.
-
autofocus
= None¶ Add autofocus attributed to the input.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
PostlabeledInputField
(**kw)[source]¶ Inherits InputField, but with a text label that follows the input field
-
text
= None¶ Text to display in the label after the field.
-
text_attrs
= {}¶ Attributes of the label displayed after to the field.
-
-
class
tw2.forms.widgets.
TextField
(**kw)[source]¶ A simple text field where to input a single line of text
-
size
= None¶ Add size attribute to the HTML field.
-
-
class
tw2.forms.widgets.
TextArea
(**kw)[source]¶ A multiline text area
-
rows
= None¶ Add a rows= attribute to the HTML textarea
-
cols
= None¶ Add a cols= attribute to the HTML textarea
-
-
class
tw2.forms.widgets.
CheckBox
(**kw)[source]¶ A single checkbox.
Its value will be True or Folse if selected or not.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
RadioButton
(**kw)[source]¶ A single radio button
-
checked
= False¶ If the radio button is checked or not.
-
-
class
tw2.forms.widgets.
PasswordField
(**kw)[source]¶ A password field. This never displays a value passed into the widget, although it does redisplay entered values on validation failure. If no password is entered, this validates as EmptyField.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
FileValidator
(**kw)[source]¶ Validate a file upload field
- extension
- Allowed extension for the file
-
class
tw2.forms.widgets.
FileField
(**kw)[source]¶ A field for uploading files. The returned object has (at least) two properties of note:
- filename:
- the name of the uploaded file
- value:
- a bytestring of the contents of the uploaded file, suitable for being written to disk
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
class
tw2.forms.widgets.
HiddenField
(**kw)[source]¶ A hidden field.
Typically this is used to bring around in the form values that the user should not be able to modify or see. Like the ID of the entity edited by the form.
-
class
tw2.forms.widgets.
IgnoredField
(**kw)[source]¶ A hidden field. The value is never included in validated data.
-
class
tw2.forms.widgets.
LabelField
(**kw)[source]¶ A read-only label showing the value of a field. The value is stored in a hidden field, so it remains through validation failures. However, the value is never included in validated data.
-
class
tw2.forms.widgets.
LinkField
(**kw)[source]¶ A dynamic link based on the value of a field. If either link or text contain a $, it is replaced with the field value. If the value is None, and there is no default, the entire link is hidden.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
Button
(**kw)[source]¶ Generic button. You can override the text using value and define a JavaScript action using attrs[‘onclick’].
-
class
tw2.forms.widgets.
ImageButton
(**kw)[source]¶ -
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
HTML5PatternMixin
(**kw)[source]¶ HTML5 mixin for input field regex pattern matching
See http://html5pattern.com/ for common patterns.
TODO: Configure server-side validator
-
class
tw2.forms.widgets.
HTML5MinMaxMixin
(**kw)[source]¶ HTML5 mixin for input field value limits
TODO: Configure server-side validator
-
class
tw2.forms.widgets.
EmailField
(**kw)[source]¶ An email input field (HTML5 only).
Will fallback to a normal text input field on browser not supporting HTML5.
-
class
tw2.forms.widgets.
UrlField
(**kw)[source]¶ An url input field (HTML5 only).
Will fallback to a normal text input field on browser not supporting HTML5.
-
class
tw2.forms.widgets.
NumberField
(**kw)[source]¶ A number spinbox (HTML5 only).
Will fallback to a normal text input field on browser not supporting HTML5.
-
class
tw2.forms.widgets.
RangeField
(**kw)[source]¶ A number slider (HTML5 only).
Will fallback to a normal text input field on browser not supporting HTML5.
-
class
tw2.forms.widgets.
SearchField
(**kw)[source]¶ A search box (HTML5 only).
Will fallback to a normal text input field on browser not supporting HTML5.
-
class
tw2.forms.widgets.
ColorField
(**kw)[source]¶ A color picker field (HTML5 only).
Will fallback to a normal text input field on browser not supporting HTML5.
-
class
tw2.forms.widgets.
SelectionField
(**kw)[source]¶ Base class for single and multiple selection fields.
The options parameter must be a list; it can take several formats:
- A list of values, e.g.
['', 'Red', 'Blue']
- A list of (code, value) tuples, e.g.
[(0, ''), (1, 'Red'), (2, 'Blue')]
- A mixed list of values and tuples. If the code is not specified, it
defaults to the value. e.g.
['', (1, 'Red'), (2, 'Blue')]
- Attributes can be specified for individual items, e.g.
[(1, 'Red', {'style':'background-color:red'})]
- A list of groups, e.g.
[('group1', [(1, 'Red')]), ('group2', ['Pink', 'Yellow'])]
Setting
value
before rendering will set the default displayed value on the page. In ToscaWidgets1, this was accomplished by settingdefault
. That is no longer the case.-
options
= None¶ List of options to pick from in the form
[(id, text), (id, text), ...]
-
prompt_text
= None¶ Prompt to display when no option is selected. Set to
None
to disable this.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
- A list of values, e.g.
-
class
tw2.forms.widgets.
MultipleSelectionField
(**kw)[source]¶ -
item_validator
= None¶ Validator that has to be applied to each item.
-
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
SingleSelectField
(**kw)[source]¶ Specialised
SelectionField
to pick one element from a list of options.
-
class
tw2.forms.widgets.
MultipleSelectField
(**kw)[source]¶ Specialised
SelectionField
to pick multiple elements from a list of options.-
size
= None¶ Number of options to show
-
-
class
tw2.forms.widgets.
SelectionTable
(**kw)[source]¶ -
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
VerticalSelectionTable
(**kw)[source]¶ -
prepare
()[source]¶ This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
-
class
tw2.forms.widgets.
BaseLayout
(**kw)[source] The following CSS classes are used, on the element containing both a child widget and its label.
- odd / even
- On alternating rows. The first row is odd.
- required
- If the field is a required field.
- error
- If the field contains a validation error.
-
prepare
()[source] Propagate the value for this widget to the children, based on their id.
-
class
tw2.forms.widgets.
TableLayout
(**kw)[source] Arrange widgets and labels in a table.
The following CSS classes are used, on the element containing both a child widget and its label.
- odd / even
- On alternating rows. The first row is odd.
- required
- If the field is a required field.
- error
- If the field contains a validation error.
-
class
tw2.forms.widgets.
ListLayout
(**kw)[source] Arrange widgets and labels in a list.
The following CSS classes are used, on the element containing both a child widget and its label.
- odd / even
- On alternating rows. The first row is odd.
- required
- If the field is a required field.
- error
- If the field contains a validation error.
-
class
tw2.forms.widgets.
RowLayout
(**kw)[source] Arrange widgets in a table row. This is normally only useful as a child to GridLayout.
-
prepare
()[source] Propagate the value for this widget to the children, based on their id.
-
-
class
tw2.forms.widgets.
GridLayout
(**kw)[source] Arrange labels and multiple rows of widgets in a grid.
-
child
alias of
tw2.core.params.RowLayout_s
-
-
class
tw2.forms.widgets.
Spacer
(**kw)[source]¶ A blank widget, used to insert a blank row in a layout.
-
class
tw2.forms.widgets.
Label
(**kw)[source]¶ A textual label. This disables any label that would be displayed by a parent layout.
-
class
tw2.forms.widgets.
Form
(**kw)[source] A form, with a submit button. It’s common to pass a TableLayout or ListLayout widget as the child.
-
classmethod
post_define
()[source] This is a class method, that is called when a subclass of this Widget is created. Process static configuration here. Use it like this:
class MyWidget(LeafWidget): @classmethod def post_define(cls): id = getattr(cls, 'id', None) if id and not id.startswith('my'): raise pm.ParameterError("id must start with 'my'")
post_define should always cope with missing data - the class may be an abstract class. There is no need to call super(), the metaclass will do this automatically.
-
submit
alias of
tw2.core.params.SubmitButton_s_s
-
prepare
()[source] This is an instance method, that is called just before the Widget is displayed. Process request-local configuration here. For efficiency, widgets should do as little work as possible here. Use it like this:
class MyWidget(Widget): def prepare(self): super(MyWidget, self).prepare() self.value = 'My: ' + str(self.value)
-
classmethod
-
class
tw2.forms.widgets.
FieldSet
(**kw)[source]¶ A field set. It’s common to pass a TableLayout or ListLayout widget as the child.
-
class
tw2.forms.widgets.
TableForm
(**kw)[source]¶ Equivalent to a Form containing a TableLayout.
-
child
¶ alias of
tw2.core.params.TableLayout_s
-
submit
¶ alias of
tw2.core.params.SubmitButton_s_s_s
-
-
class
tw2.forms.widgets.
ListForm
(**kw)[source]¶ Equivalent to a Form containing a ListLayout.
-
child
¶ alias of
tw2.core.params.ListLayout_s
-
submit
¶ alias of
tw2.core.params.SubmitButton_s_s_s
-
-
class
tw2.forms.widgets.
TableFieldSet
(**kw)[source]¶ Equivalent to a FieldSet containing a TableLayout.
-
child
¶ alias of
tw2.core.params.TableLayout_s
-