Skip to content

Commit

Permalink
Validation of Forms
Browse files Browse the repository at this point in the history
  • Loading branch information
amol- committed Apr 3, 2019
1 parent 4940bb5 commit 15e2ea0
Showing 1 changed file with 133 additions and 7 deletions.
140 changes: 133 additions & 7 deletions docs/source/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ to specify where the form should be submitted.
you probably want the action to be a ``tg.lurl`` to
ensure that prefix of your application is retained.

Submitting Forms
~~~~~~~~~~~~~~~~

Here validation and submitting...

Form Buttons
~~~~~~~~~~~~

Expand Down Expand Up @@ -151,6 +146,137 @@ a text field that suggests the placeholder based on its original value::
.. autoclass:: tw2.forms.widgets.Form
:members:

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 :ref:`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 :meth:`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 :class:`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.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 :meth:`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 :attr:`tw2.core.validation.ValidationError.widget`
to access each one of them.

.. _formlayout:

Form Layout
Expand Down Expand Up @@ -239,8 +365,8 @@ the children using ``w.children.child_name``::
.. autoclass:: tw2.forms.widgets.RowLayout
:members:

Bultin widgets
--------------------------
Bultin Form Fields
------------------

``tw2.forms`` package comes with a bunch of builtin
widgets that can help you build the most common kind
Expand Down

0 comments on commit 15e2ea0

Please sign in to comment.