Validation

ToscaWidgets provides validation support for all the data that needs to be displayed into widgets or that has to come from submitted forms.

Setting a validator for a widget (or a form field) can be done through the tw2.core.Widget.validator param.

Validators are typically used in the context of forms and can be used both to tell ToscaWidgets how a python object should be displayed in HTML result:

>>> import tw2.core as twc
>>> import tw2.forms as twf
>>>
>>> w = twf.TextField(validator=twc.validation.DateValidator(format="%Y/%m/%d"))
>>> w.display(datetime.datetime.utcnow())
Markup('<input value="2019/04/04" type="text"/>')

Or to tell ToscaWidgets how the data coming from a submitted form should be converted into Python:

>>> class MyDateForm(twf.Form):
...   class child(twf.TableLayout):
...     date = twf.TextField(validator=twc.validation.DateValidator(format="%Y/%m/%d"))
...
>>> MyDateForm.validate({'date': '2019/5/3'})
{'date': datetime.date(2019, 5, 3)}

Validators

A validator is a class in charge of two major concerns:

  • Converting data from the web to python and back to the web
  • Validating that the data is what you expected.

Both those step are performed through two methods:

tw2.core.validation.Validator.to_python() which is in charge of converting data from the web to Python:

>>> validator = twc.validation.DateValidator(required=True, format="%Y/%m/%d")
>>> validator.to_python('2019/10/3')
datetime.date(2019, 10, 3)

and tw2.core.validation.Validator.from_python() which is in charge of converting data from Python to be displahyed on a web page:

>>> validator.from_python(datetime.datetime.utcnow())
"2019/04/04"

When converting data to python (so for data submitted from the web to your web application) the validator does three steps:

  1. Ensures that the data is not empty through tw2.core.validation.Validator._is_empty() if required=True was provided
  2. Converts data to Python through tw2.core.validation.Validator._convert_to_python()
  3. Validates that the converted data matches what you expected through tw2.core.validation.Validator._validate_python()

All those three methods (is_empty, _convert_to_python and _validate_python) can be specialised in subclasses to implement your own validators.

For example the tw2.core.validation.IntValidator takes care of converting the incoming text to intergers:

>>> twc.validation.IntValidator().to_python("5")
5

but also takes care of validating that it’s within an expected range:

>>> twc.validation.IntValidator(min=1).to_python("0")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 236, in to_python
    self._validate_python(value, state)
File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 376, in _validate_python
    raise ValidationError('toosmall', self)
tw2.core.validation.ValidationError: Must be at least 1

Custom Validators

You can write your own validators by subclassing tw2.core.validation.Validator.

Those should at least implement the custom conversion part, to tell toscawidgets how to convert the incoming data to the type you expect:

class TwoNumbersValidator(twc.validation.Validator):
    def _convert_to_python(self, value, state=None):
        try:
            return [int(v) for v in value.split(',')]
        except ValueError:
            raise twc.validation.ValidationError("Must be integers", self)
        except Exception:
            raise twc.validation.ValidationError("corrupt", self)

This is already enough to be able to convert the incoming data to a list of numbers:

>>> TwoNumbersValidator().to_python("5,3")
[5, 3]

and to detect that numbers were actually submitted:

>>> TwoNumbersValidator().to_python("5, allo")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 235, in to_python
        value = self._convert_to_python(value, state)
    File "<stdin>", line 6, in _convert_to_python
tw2.core.validation.ValidationError: Must be integers

and to detect malformed inputs:

>>> TwoNumbersValidator().to_python(datetime.datetime.utcnow())
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 235, in to_python
        value = self._convert_to_python(value, state)
    File "<stdin>", line 8, in _convert_to_python
tw2.core.validation.ValidationError: Form submission received corrupted; please try again

But it doesn’t perform validation on the converted data. It doesn’t ensure that what was provided are really two numbers:

>>> TwoNumbersValidator().to_python("5")
[5]

To do so we need to implement the validation part of the validator, which is done through _validate_python:

class TwoNumbersValidator(twc.validation.Validator):
    def _convert_to_python(self, value, state=None):
        try:
            return [int(v) for v in value.split(',')]
        except ValueError:
            raise twc.validation.ValidationError("Must be integers", self)
        except Exception:
            raise twc.validation.ValidationError("corrupt", self)

    def _validate_python(self, value, state=None):
        if len(value) != 2:
            raise twc.validation.ValidationError("Must be two numbers", self)

To finally provide coverage for the case where a single number (or more than two numbers) were provided:

>>> TwoNumbersValidator().to_python("5")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 236, in to_python
        self._validate_python(value, state)
    File "<stdin>", line 11, in _validate_python
tw2.core.validation.ValidationError: Must be two numbers

You will notice by the way, that empty values won’t cause validation errors:

>>> v = TwoNumbersValidator().to_python("")

Those will be converted to None:

>>> print(v)
None

Because by default validators have required=False which means that missing values are perfectly fine.

If you want to prevent that behaviour you can provide required=True to the validator:

>>> TwoNumbersValidator(required=True).to_python("")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 231, in to_python
        raise ValidationError('required', self)
tw2.core.validation.ValidationError: Enter a value

Internationalization

Validator error messages can be translated through the usage of the msgs lookup dictionary.

The msgs dictionary is a map from keywords to translated strings and it’s used by ToscaWidgets to know which message to show to users:

from tw2.core.i18n import tw2_translation_string

class FloatValidator(twc.Validator):
    msgs = {
        "notfloat": tw2_translation_string("Not a floating point number")
    }

    def _convert_to_python(self, value, state):
        try:
            return float(value)
        except ValueError:
            raise twc.validation.ValidationError("notfloat", self)

You will see that when validation fails, the "notfloat" key is looked up into msgs to find the proper message:

>>> FloatValidator().to_python("Hello")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 235, in to_python
        value = self._convert_to_python(value, state)
    File "<stdin>", line 9, in _convert_to_python
tw2.core.validation.ValidationError: Not a floating point number

The entry in msgs is then wrapped in a tw2.core.i18n.tw2_translation_string() call to ensure it gets translated using the translated that was configured in tw2.core.middleware.TwMiddleware options.

Note

tw2.core.i18n.tw2_translation_string() is also available as tw2.core.i18n._ so that frameworks that automate translatable strings collection like Babel can more easily find strings that need translation in ToscaWidgets validators.

The other purpose of msgs is to allow users of your validator to customise their error messages:

>>> FloatValidator(msgs={"notfloat": "Ahah! Gotcha!"}).to_python("Hello")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/amol/wrk/tw2.core/tw2/core/validation.py", line 235, in to_python
        value = self._convert_to_python(value, state)
    File "<stdin>", line 9, in _convert_to_python
tw2.core.validation.ValidationError: Ahah! Gotcha!

In such case (when msgs are customised) translation of the messages is up to the user customising them. Who might want to ensure the new provided messages are still wrapped in tw2.core.i18n.tw2_translation_string().

Builtin Validators

exception tw2.core.validation.ValidationError(msg, validator=None, widget=None)[source]

Invalid data was encountered during validation.

The constructor can be passed a short message name, which is looked up in a validator’s msgs dictionary. Any values in this, like $val` are substituted with that attribute from the validator. An explicit validator instance can be passed to the constructor, or this defaults to Validator otherwise.

message

Added for backwards compatibility. Synonymous with msg.

tw2.core.validation.catch

alias of tw2.core.validation.ValidationError

tw2.core.validation.unflatten_params(params)[source]

This performs the first stage of validation. It takes a dictionary where some keys will be compound names, such as “form:subform:field” and converts this into a nested dict/list structure. It also performs unicode decoding, with the encoding specified in the middleware config.

class tw2.core.validation.Validator(**kw)[source]

Base class for validators

required
Whether empty values are forbidden in this field. (default: False)
strip
Whether to strip leading and trailing space from the input, before any other validation. (default: True)

To convert and validate a value to Python, use the to_python() method, to convert back from Python, use from_python().

To create your own validators, sublass this class, and override any of _validate_python(), _convert_to_python(), or _convert_from_python(). Note that these methods are not meant to be used externally. All of them may raise ValidationErrors.

to_python(value, state=None)[source]

Convert an external value to Python and validate it.

from_python(value, state=None)[source]

Convert from a Python object to an external value.

validate_python(value, state=None)[source]

“Deprecated, use _validate_python() instead.

This method has been renamed in FormEncode 1.3 and ToscaWidgets 2.2 in order to clarify that is an internal method that is meant to be overridden only; you must call meth:to_python to validate values.

class tw2.core.validation.BlankValidator(**kw)[source]

Always returns EmptyField. This is the default for hidden fields, so their values are not included in validated data.

to_python(value, state=None)[source]

Convert an external value to Python and validate it.

class tw2.core.validation.LengthValidator(**kw)[source]

Confirm a value is of a suitable length. Usually you’ll use StringLengthValidator or ListLengthValidator instead.

min
Minimum length (default: None)
max
Maximum length (default: None)
class tw2.core.validation.StringLengthValidator(**kw)[source]

Check a string is a suitable length. The only difference to LengthValidator is that the messages are worded differently.

class tw2.core.validation.ListLengthValidator(**kw)[source]

Check a list is a suitable length. The only difference to LengthValidator is that the messages are worded differently.

class tw2.core.validation.RangeValidator(**kw)[source]

Confirm a value is within an appropriate range. This is not usually used directly, but other validators are derived from this.

min
Minimum value (default: None)
max
Maximum value (default: None)
class tw2.core.validation.IntValidator(**kw)[source]

Confirm the value is an integer. This is derived from RangeValidator so min and max can be specified.

class tw2.core.validation.BoolValidator(**kw)[source]

Convert a value to a boolean. This is particularly intended to handle check boxes.

class tw2.core.validation.OneOfValidator(**kw)[source]

Confirm the value is one of a list of acceptable values. This is useful for confirming that select fields have not been tampered with by a user.

values
Acceptable values
class tw2.core.validation.DateTimeValidator(**kw)[source]

Confirm the value is a valid date and time. This is derived from RangeValidator so min and max can be specified.

format
The expected date/time format. The format must be specified using the same syntax as the Python strftime function.
class tw2.core.validation.DateValidator(**kw)[source]

Confirm the value is a valid date.

Just like DateTimeValidator, but without the time component.

class tw2.core.validation.RegexValidator(**kw)[source]

Confirm the value matches a regular expression.

regex
A Python regular expression object, generated like re.compile('^\w+$')
class tw2.core.validation.EmailValidator(**kw)[source]

Confirm the value is a valid email address.

class tw2.core.validation.UrlValidator(**kw)[source]

Confirm the value is a valid URL.

class tw2.core.validation.IpAddressValidator(**kw)[source]

Confirm the value is a valid IP4 address, or network block.

allow_netblock
Allow the IP address to include a network block (default: False)
require_netblock
Require the IP address to include a network block (default: False)
class tw2.core.validation.UUIDValidator(**kw)[source]

Confirm the value is a valid uuid and convert to uuid.UUID.

class tw2.core.validation.MatchValidator(other_field, pass_on_invalid=False, **kw)[source]

Confirm a field matches another field

other_field
Name of the sibling field this must match
pass_on_invalid
Pass validation if sibling field is Invalid
class tw2.core.validation.CompoundValidator(*args, **kw)[source]

Base class for compound validators.

Child classes Any and All take validators as arguments and use them to validate “value”. In case the validation fails, they raise a ValidationError with a compound message.

>>> v = All(StringLengthValidator(max=50), EmailValidator, required=True)
class tw2.core.validation.All(*args, **kw)[source]

Confirm all validators passed as arguments are valid.

class tw2.core.validation.Any(*args, **kw)[source]

Confirm at least one of the validators passed as arguments is valid.