Validators and Checks

Validators allow you to provide users feedback on their input through structured, reusable callables. Validators can be supplied an arbitrary number of inputs as well as dispatch information (errors, warnings, etc) to an arbitrary number of output Nodes.

Using Validators In Your Form

Validators are generally added into your Form schema in a way similar to adding Nodes; that is, by declaring attributes in your Form definition. There is a long syntax that is more explicit as well as a shorthand that can add convenience for simple validators. The explicit declaration can be seen below through the definition of a Check.

class MyForm(yota.Form):
    # This syntax shortens up the above explicit syntax for simple
    # validators
    first = EntryNode(title='First name')
    _first_valid = Check(MinLengthValidator(5), 'first')

The syntax above defines a single yota.nodes.EntryNode and an associated validator that ensures the entered value is at least 5 characters long. This is done through the declaration of a yota.Check object. The Check accepts the actual validator as its first argument, followed by the names of Nodes that you will be validating. The above example binds our yota.validators.MinLengthValidator to a Node with the attribute name ‘first’. Later when we try to validate the Form the string ‘first’ will be used to lookup our Node and supply the appropriate information to the validator method. Nodes in Yota are identified by their attribute name as given in the class declaration. However, If you later add a Node dynamically it will need to specify the _attr_name attribute upon declaration explicitly. More on this in Dynamic Forms.

The above syntax gives us some nice power. We can supply that validation method with as many Nodes as we would like in a clear way. But what if we want to write a bunch of validators that only validate a single Node? Then the above is quite verbose, and below shows an implicit declaration that is a nice option for simple validators, and is just syntactic sugar for the above syntax.

class MyForm(yota.Form):
    # This syntax shortens up the above explicit syntax for simple
    # validators. An arg of 'first' will automatically be added to the
    # Check object for you.
    first = EntryNode(title='First name',
                        validators=Check(MinLengthValidator(5)))

    # This even more brief syntax will automatically build the Check
    # object for you since it's just boilerplate at this point
    last = EntryNode(title='Last name', validator=MinLengthValidator(5)

    # This syntax however is just like above. Be aware that your
    # attribute name will not be automatically added since your
    # explicitly defining args
    address = EntryNode(validators=
                Check(MinLengthValidator(9), 'address'))

    # In addition, you can specify a list of validators, or a tuple
    addr = EntryNode(title='Address', validators=[MinLengthValidator(5),
                                                 MaxLengthValidator(25)])

Note

If neither kwargs or args are specified and cannot be implicitly determined an exception will be thrown.

Validator Execution

With the regular form validation method Form.validate_render() the error values after validation are maintained in errors and passed into the rendering context. In your Node template, the error can then be used for anything related to rendering and will contain exactly what was returned by your validator.

With either the piecewise JSON validation method or the regular JSON validation method the data will get translated into JSON. This JSON string is designed to be passed back via an AJAX request and fed into Yota’s JacaScript jQuery plugin, although it could be used in other ways. Details about this functionality are in the AJAX documentation section.

To continue our example series above, we may now try and execute a validation action on our Form. For this example we will use a Flask view, although the concepts should be fairly obvious and transfer to most frameworks easily.

class MyForm(yota.Form):
    # Our same form definition as above but stripped of the now un-needed
    # comments
    first = EntryNode(title='First name',
                        validators=Check(MinLengthValidator(5)))
    last = EntryNode(title='Last name', validators=MinLengthValidator(5)
    address = EntryNode(validators=
                Check(MinLengthValidator(9), 'address'))

# In Flask routes are declared with annotations. Basically mapping a URL to
# this method
@app.route("/ourform", methods=['GET', 'POST'])
def basic():
    # Create an instance of our Form class
    form = MyForm()

    # When the form is submitted to this URL (by default forms submit to
    # themselves)
    if request.method == 'POST':
        # Run our convenience method designed for regular forms
        # 'success' if validation passed, 'out' is the re-rendering of the form
        success, out = form.validate_render(request.form)

        # if validation passed, we should be doing something
        if success:
            # Load up our validated data
            data = form.get_by_attribute()

            # Create a pretend SQLAlchemy object. Basically, we want to try
            # and save the data somehow...
            res = User(first=data['first'],
                       last=data['last'],
                       address=data['address'])

            # Attempt to save our changes
            try:
                DBSession.add(new_user)
                DBSession.commit()
            except (sqlalchemy.exc, sqlalchemy.orm.exc) as e:
                # An error with our query has occurred, change the message
                # and update our rendered output
                out = form.update_success(
                    {'message': ('An error with our database occurred!')})
    else:
        # By default we just render an empty form
        out = form.render()

    def success_header_generate(self):
        return {'message': 'Thanks for your submission!'}

    return render_template('basic.html',
                            form=out)

Making Custom Validators

A validator should be a Python callable. The callable will be accessed through a Check object that provides context on how you would like your validator to be executed in this given instance. Checks are what provide your validation callable with the data it is going to validate. Essentially they are context resolvers, which is part of what allows Yota to be so dynamic.

When the validation callable is run it is supplied with a reference to a Node. The submitted data that is associated with that Node will be loaded into the data attribute automatically. At this point, perhaps an example will help clarify.

import yota

def MyValidator(node_in):
    if len(node_in.data) > 5:
        node_in.add_error({'message': "You're text is too long!"})

class MyForm(yota.Form):
    test_node = yota.nodes.EntryNode()
    _test_check = yota.validators.Check(MyValidator, 'test_node')

In the above exmaple we made a simple validator that throws an error if your input value is longer than 5 characters. You can see the creation of the Check instance in the Form declaration supplies the string ‘test_node’. This is indicating the name of the Node that you would like to supply to the Validator as input.

Note

In Yota, all Nodes are uniquely identified by an attribute _attr_name. This gets automatically set to the value of the attribute you assigned the Node to in your Form declaration.

Later when the validator is to be called the string is replaced by a refernce to a Node with the specified Node._attr_name. The method behind this maddness is that it allows for dynamically adding Nodes at and up until vaildation time, as well as dynamic injection of validation rules themselves. In addition your validation methods can now request as much data as you’d like, and subsequently can disperse errors to any Nodes they are supplied with.

Return Semantics

Validators need not return anything explicitly, but instead provide output by appending error information to one of their supplied Node’s errors list attribute via the method Node.add_error(). This method is simply a wrapper around appending to a list so that different ordering or filtering semantics may be used if desired. The data can be put into this list is fairly flexible, although a dictionary is recommended. If you are running a JSON based validation method the data must by serializable, otherwise it may be anything since it is merely passed into the rendering context of your templates.

That said, the builtin templtes are setup to recieve specific things. The default templates are setup to look two keys: a dictionary with a single key ‘message’ which will be printed, and ‘type’ to denote the alert style. If a type is ommitted then type will default to an error for rendering purposes. Looking at a builtin validator should provide additional clarity.

class IntegerValidator(object):
    """ Checks if the value is an integer and converts it to one if it is

    :param message: (optional) The message to present to the user upon failure.
    :type message: string
    """
    # A minor optimization that is borderline silly
    __slots__ = ["message"]

    def __init__(self, message=None):
        self.message = message if message else "Value must only contain numbers"
        super(IntegerValidator, self).__init__()

    def __call__(self, target):
        # This provides a conversion as well as a validation
        try:
            target.data = int(target.data)
        except ValueError:
            # Type can be safely ommitted because this is an error
            target.add_error({'message': self.message})

For rendering errors you may notice the _type_class key being looked for in the error.html template. This is generated internally from what you enter as ‘type’ in your return dictionary. This is resolved by the Form.type_class_map, which maps types in the key to classes to be applied in the value. An example usage might be that you’d like to add your own class to the error display.

Note

If you wish to make use of Special Key Values you will be required to use dictionaries to return errors.

Special Key Values

Block
If set to False the validation message will not prevent the form from submitting. As might be expected, a single blocking validator will cause the block flag to return true. This is useful for things like notification of password strength, etc. Errors returned are assumed to be blocking unless specified otherwise.

Builtin Validators

The default pattern for builtin Validators in Yota is to return a dictionary with a key ‘message’ containing the error. This is also the pattern that the builtin Node‘s except when rendering errors, and therefore is the recommended format when building your own validators.

class yota.validators.MinLengthValidator(length, message=None)[source]

Checks to see if data is at least length long.

Parameters:
  • length (integer) – The minimum length of the data.
  • message (string) – The message to present to the user upon failure.
class yota.validators.MaxLengthValidator(length, message=None)[source]

Checks to see if data is at most length long.

Parameters:
  • length (integer) – The maximum length of the data.
  • message (string) – The message to present to the user upon failure.
class yota.validators.MinMaxValidator(min, max, minmsg=None, maxmsg=None)[source]

Checks if the value is between the min and max values given

Parameters:
  • message (string) – (optional) The message to present to the user upon failure.
  • min – The minimum length of the data.
  • max – The maximum length of the data.
class yota.validators.RequiredValidator(message=None)[source]

Checks to make sure the user entered something.

Parameters:message (string) – (optional) The message to present to the user upon failure.
class yota.validators.RegexValidator(regex=None, message=None)[source]

Quick and easy check to see if the input matches the given regex.

Parameters:
  • regex (string) – (optional) The regex to run against the input.
  • message (string) – (optional) The message to present to the user upon failure.
class yota.validators.EmailValidator(message=None)[source]

A direct port of the Django Email validator. Checks to see if an email is valid using regular expressions.

class yota.validators.UsernameValidator(message=None)[source]

Quick and easy check to see if a field matches a stamdard username regex. This regex matches a string from 3-20 characters long and composed only of numbers, letters, hyphens, and underscores.

Parameters:message (string) – (optional) The message to present to the user upon failure.
class yota.validators.PasswordStrengthValidator(regex=None, message=None)[source]

A validator to check the password strength.

Parameters:
  • regex (list) – (optional) The regex to run against the input.
  • message (string) – (optional) The message to present to the user upon failure.
class yota.validators.MatchingValidator(message=None)[source]

Checks if two nodes values match eachother. The error is delivered to the first node.

Parameters:message (string) – (optional) The message to present to the user upon failure.
class yota.validators.IntegerValidator(message=None)[source]

Checks if the value is an integer and converts it to one if it is

Parameters:message (string) – (optional) The message to present to the user upon failure.

Check API

class yota.Check(callable, *args, **kwargs)

This object wraps a validator callable and is intended to be used in your Form subclass definition.

Parameters:
  • validator (callable) – This is required to be a callable object that will carry out the actual validation. Many generic validators exist, or you can roll your own.
  • args (list) – A list of strings, or a single string, representing that _attr_name of the Node you would like passed into the validator. Once a validator is called this string will get resolved into the Node object
  • kwargs (dict) – Same as args above except it allows passing in node information as keyword arguments to the validator callable.

Check objects are designed to be declared in your form subclass.

node_visited(visited)

Used by piecewise validation to determine if all the Nodes involved in the validator have been “visited” and thus are ready for the validator to be run

Project Versions

Table Of Contents

Previous topic

Nodes

Next topic

Renderers

This Page