Validators

Validators are classes used to validate input fields (including forms generated from database tables). With the advanced forms derived from SQLFORM, validators create widgets such as drop-down menus and lookups from other tables.

Here is an example of using a validator with a FORM field:

  1. INPUT(_name='a', requires=IS_INT_IN_RANGE(0, 10))

Here is an example of how to require a validator for a table field:

  1. db.define_table('person', Field('name'))
  2. db.person.name.requires = IS_NOT_EMPTY()

Validators are always assigned using the requires attribute of a field. A field can have a single validator or multiple validators. Multiple validators are made part of a list:

  1. db.person.name.requires = [IS_NOT_EMPTY(),
  2. IS_NOT_IN_DB(db, 'person.name')]

Normally validators are called automatically by the function accepts and process of a FORM or other HTML helper object that contains a form. They are called in the order in which they are listed.

One can also call validators explicitly for a field:

  1. db.person.name.validate(value)

which returns a tuple (value, error) and error is None if the value validates.

Built-in validators have constructors that take an optional argument:

  1. IS_NOT_EMPTY(error_message='cannot be empty')

error_message allows you to override the default error message for any validator.

Here is an example of a validator on a database table:

  1. db.person.name.requires = IS_NOT_EMPTY(error_message='fill this!')

where we have used the translation operator T to allow for internationalization. Notice that default error messages are not translated.

Mind that the only validators that can be used with list: type fields are:

  • IS_IN_DB(..., multiple=True)
  • IS_IN_SET(..., multiple=True)
  • IS_NOT_EMPTY()
  • IS_LIST_OF_EMAILS()
  • IS_LIST_OF(...)

The latter can be used to apply any validator to the individual items in the list. multiple=(1, 1000) requires a selection of between 1 and 1000 items. This enforces selection of at least one choice.

Text format validators

IS_ALPHANUMERIC

This validator checks that a field value contains only characters in the ranges a-z, A-Z, or 0-9.

  1. requires = IS_ALPHANUMERIC(error_message='must be alphanumeric!')
IS_LOWER

This validator never returns an error. It just converts the value to lower case.

  1. requires = IS_LOWER()
IS_UPPER

This validator never returns an error. It converts the value to upper case.

  1. requires = IS_UPPER()
IS_EMAIL

It checks that the field value looks like an email address. It does not try to send email to confirm.

  1. requires = IS_EMAIL(error_message='invalid email!')
IS_MATCH

This validator matches the value against a regular expression and returns an error if it does not match. Here is an example of usage to validate a US zip code:

  1. requires = IS_MATCH('^\d{5}(-\d{4})?$',
  2. error_message='not a zip code')

Here is an example of usage to validate an IPv4 address (note: the IS_IPV4 validator is more appropriate for this purpose):

  1. requires = IS_MATCH('^\d{1,3}(.\d{1,3}){3}$',
  2. error_message='not an IP address')

Here is an example of usage to validate a US phone number:

  1. requires = IS_MATCH('^1?((-)\d{3}-?|\(\d{3}\))\d{3}-?\d{4}$',
  2. error_message='not a phone number')

For more information on Python regular expressions, refer to the official Python documentation.

IS_MATCH takes an optional argument strict which defaults to False. When set to True it only matches the whole string (from the beginning to the end):

  1. >>> IS_MATCH('ab', strict=False)('abc')
  2. ('abc', None)
  3. >>> IS_MATCH('ab', strict=True)('abc')
  4. ('abc', 'Invalid expression')

IS_MATCH takes an other optional argument search which defaults to False. When set to True, it uses regex method search instead of method match to validate the string.

IS_MATCH('...', extract=True) filters and extract only the first matching substring rather than the original value.

IS_LENGTH

Checks if length of field’s value fits between given boundaries. Works for both text and file inputs.

Its arguments are:

  • maxsize: the maximum allowed length / size (has default = 255)
  • minsize: the minimum allowed length / size

Examples: Check if text string is shorter than 33 characters:

  1. INPUT(_type='text', _name='name', requires=IS_LENGTH(32))

Check if password string is longer than 5 characters:

  1. INPUT(_type='password', _name='name', requires=IS_LENGTH(minsize=6))

Check if uploaded file has size between 1KB and 1MB:

  1. INPUT(_type='file', _name='name', requires=IS_LENGTH(1048576, 1024))

For all field types except for files, it checks the length of the value. In the case of files, the value is a cgi.FieldStorage, so it validates the length of the data in the file, which is the behavior one might intuitively expect.

IS_URL

Rejects a URL string if any of the following is true:

  • The string is empty or None
  • The string uses characters that are not allowed in a URL
  • The string breaks any of the HTTP syntactic rules
  • The URL scheme specified (if one is specified) is not ‘http’ or ‘https’
  • The top-level domain (if a host name is specified) does not exist

(These rules are based on RFC 2616[RFC2616] )

This function only checks the URL’s syntax. It does not check that the URL points to a real document, for example, or that it otherwise makes semantic sense. This function does automatically prepend ‘http://‘ in front of a URL in the case of an abbreviated URL (e.g. ‘google.ca’).

If the parameter mode=’generic’ is used, then this function’s behavior changes. It then rejects a URL string if any of the following is true:

  • The string is empty or None
  • The string uses characters that are not allowed in a URL
  • The URL scheme specified (if one is specified) is not valid

(These rules are based on RFC 2396[RFC2396] )

The list of allowed schemes is customizable with the allowed_schemes parameter. If you exclude None from the list, then abbreviated URLs (lacking a scheme such as ‘http’) will be rejected.

The default prepended scheme is customizable with the prepend_scheme parameter. If you set prepend_scheme to None, then prepending will be disabled. URLs that require prepending to parse will still be accepted, but the return value will not be modified.

IS_URL is compatible with the Internationalized Domain Name (IDN) standard specified in RFC 3490[RFC3490] ). As a result, URLs can be regular strings or unicode strings. If the URL’s domain component (e.g. google.ca) contains non-US-ASCII letters, then the domain will be converted into Punycode (defined in RFC 3492[RFC3492] ). IS_URL goes a bit beyond the standards, and allows non-US-ASCII characters to be present in the path and query components of the URL as well. These non-US-ASCII characters will be encoded. For example, space will be encoded as’%20’. The unicode character with hex code 0x4e86 will become ‘%4e%86’.

Examples:

  1. requires = IS_URL())
  2. requires = IS_URL(mode='generic')
  3. requires = IS_URL(allowed_schemes=['https'])
  4. requires = IS_URL(prepend_scheme='https')
  5. requires = IS_URL(mode='generic',
  6. allowed_schemes=['ftps', 'https'],
  7. prepend_scheme='https')
IS_SLUG
  1. requires = IS_SLUG(maxlen=80, check=False, error_message='must be slug')

If check is set to True it check whether the validated value is a slug (allowing only alphanumeric characters and non-repeated dashes).

If check is set to False (default) it converts the input value to a slug.

IS_JSON
  1. requires = IS_JSON(error_message='Invalid json', native_json=False)

This validator checks that a field value is in JSON format.

If native_json is set to False (default) it converts the input value to the serialized value otherwise the input value is left unchanged.

Date and time validators

IS_TIME

This validator checks that a field value contains a valid time in the specified format.

  1. requires = IS_TIME(error_message='must be HH:MM:SS!')
IS_DATE

This validator checks that a field value contains a valid date in the specified format. It is good practice to specify the format using the translation operator, in order to support different formats in different locales.

  1. requires = IS_DATE(format=T('%Y-%m-%d'),
  2. error_message='must be YYYY-MM-DD!')

For the full description on % directives look under the IS_DATETIME validator.

IS_DATETIME

This validator checks that a field value contains a valid datetime in the specified format. It is good practice to specify the format using the translation operator, in order to support different formats in different locales.

  1. requires = IS_DATETIME(format=T('%Y-%m-%d %H:%M:%S'),
  2. error_message='must be YYYY-MM-DD HH:MM:SS!')

The following symbols can be used for the format string (this shows the symbol, their meaning, and an example string):

  1. %Y year with century (e.g. '1963')
  2. %y year without century (e.g. '63')
  3. %d day of the month (e.g. '28')
  4. %m month (e.g '08')
  5. %b abbreviated month name (e.g.'Aug')
  6. %B full month name (e.g. 'August')
  7. %H hour (24-hour clock, e.g. '14')
  8. %I hour (12-hour clock, e.g. '02')
  9. %p either 'AM' or 'PM'
  10. %M minute (e.g. '30')
  11. %S second (e.g. '59')
IS_DATE_IN_RANGE

Works very much like the previous validator but allows to specify a range:

  1. requires = IS_DATE_IN_RANGE(format=T('%Y-%m-%d'),
  2. minimum=datetime.date(2008, 1, 1),
  3. maximum=datetime.date(2009, 12, 31),
  4. error_message='must be YYYY-MM-DD!')

For the full description on % directives look under the IS_DATETIME validator.

IS_DATETIME_IN_RANGE

Works very much like the previous validator but allows to specify a range:

  1. requires = IS_DATETIME_IN_RANGE(format=T('%Y-%m-%d %H:%M:%S'),
  2. minimum=datetime.datetime(2008, 1, 1, 10, 30),
  3. maximum=datetime.datetime(2009, 12, 31, 11, 45),
  4. error_message='must be YYYY-MM-DD HH:MM::SS!')

For the full description on % directives look under the IS_DATETIME validator.

Range, set and equality validators

IS_EQUAL_TO

Checks whether the validated value is equal to a given value (which can be a variable):

  1. requires = IS_EQUAL_TO(request.vars.password,
  2. error_message='passwords do not match')
IS_NOT_EMPTY

This validator checks that the content of the field value is neither None nor an empty string nor an empty list. A string value is checked for after a .strip().

  1. requires = IS_NOT_EMPTY(error_message='cannot be empty!')

You can provide a regular expression for the matching of the empty string.

  1. requires = IS_NOT_EMPTY(error_message='Enter a value', empty_regex='NULL(?i)')
IS_NULL_OR

Deprecated, an alias for IS_EMPTY_OR described below.

IS_EMPTY_OR

Sometimes you need to allow empty values on a field along with other requirements. For example a field may be a date but it can also be empty. The IS_EMPTY_OR validator allows this:

  1. requires = IS_EMPTY_OR(IS_DATE())

An empty value is either None or an empty string or an empty list. A string value is checked for after a .strip().

You can provide a regular expression for the matching of the empty string with the empty_regex argument (like for IS_NOT_EMPTY validator).

You may also specify a value to be used for the empty case.

  1. requires = IS_EMPTY_OR(IS_ALPHANUMERIC(), null='anonymous')
IS_EXPR

This validator let you express a general condition by means of a callable which takes a value to validate and returns the error message or None to accept the input value.

  1. requires = IS_EXPR(lambda v: T('not divisible by 3') if int(v) % 3 else None)

Notice that returned message will not be translated if you do not arrange otherwise.

For backward compatibility the condition may be expressed as a string containing a logical expression in terms of a variable value. It validates a field value if the expression evaluates to True.

  1. requires = IS_EXPR('int(value) % 3 == 0',
  2. error_message='not divisible by 3')

One should first check that the value is an integer so that an exception will not occur.

  1. requires = [IS_INT_IN_RANGE(0, None),
  2. IS_EXPR(lambda v: T('not divisible by 3') if v % 3 else None)]
IS_DECIMAL_IN_RANGE
  1. INPUT(_type='text', _name='name', requires=IS_DECIMAL_IN_RANGE(0, 10, dot="."))

It converts the input into a Python Decimal or generates an error if the decimal does not fall within the specified inclusive range. The comparison is made with Python Decimal arithmetic.

The minimum and maximum limits can be None, meaning no lower or upper limit, respectively.

The dot argument is optional and allows you to internationalize the symbol used to separate the decimals.

IS_FLOAT_IN_RANGE

Checks that the field value is a floating point number within a definite range, 0 <= value <= 100 in the following example:

  1. requires = IS_FLOAT_IN_RANGE(0, 100, dot=".",
  2. error_message='negative or too large!')

The dot argument is optional and allows you to internationalize the symbol used to separate the decimals.

IS_INT_IN_RANGE

Checks that the field value is an integer number within a definite range, 0 <= value < 100 in the following example:

  1. requires = IS_INT_IN_RANGE(0, 100,
  2. error_message='negative or too large!')
IS_IN_SET

In SQLFORM (and the grids) this validator will automatically set the form field to an option field (ie, with a drop-down menu).

IS_IN_SET checks that the field values are in a set:

  1. requires = IS_IN_SET(['a', 'b', 'c'], zero=T('choose one'),
  2. error_message='must be a or b or c')

The zero argument is optional and it determines the text of the option selected by default, an option which is not accepted by the IS_IN_SET validator itself. If you do not want a “choose one” option, set zero=None.

The elements of the set can be combined with a numerical validator, as long as IS_IN_SET is first in the list. Doing so wil force conversion by the last validator to the numerical type. So, IS_IN_SET can be followed by IS_INT_IN_RANGE (which converts the value to int) or IS_FLOAT_IN_RANGE (which converts the value to float). For example:

  1. requires = [ IS_IN_SET([2, 3, 5, 7], error_message='must be prime and less than 10'),
  2. IS_INT_IN_RANGE(0, None) ]
Checkbox validation

To force a filled-in form checkbox (such as an acceptance of terms and conditions), use this:

  1. requires=IS_IN_SET(['on'])
Dictionaries and tuples with IS_IN_SET

You may also use a dictionary or a list of tuples to make the drop down list more descriptive:

  1. Dictionary example:
  2. requires = IS_IN_SET({'A':'Apple', 'B':'Banana', 'C':'Cherry'}, zero=None)
  3. List of tuples example:
  4. requires = IS_IN_SET([('A', 'Apple'), ('B', 'Banana'), ('C', 'Cherry')])
Sorted options

To keep the options alphabetically sorted by their labels into the drop down list, use the sort argument with IS_IN_SET.

  1. IS_IN_SET([('H', 'Hulk'), ('S', 'Superman'), ('B', 'Batman')], sort=True)
IS_IN_SET and Tagging

The IS_IN_SET validator has an optional attribute multiple=False. If set to True, multiple values can be stored in one field. The field should be of type list:integer or list:string as discussed in Chapter 6. An explicit example of tagging is discussed there. We strongly suggest using the jQuery multiselect plugin to render multiple fields.

Note that when multiple=True, IS_IN_SET will accept zero or more values, i.e. it will accept the field when nothing has been selected. multiple can also be a tuple of the form (a, b) where a and b are the minimum and (exclusive) maximum number of items that can be selected respectively.

Complexity and security validators

IS_STRONG

Enforces complexity requirements on a field (usually a password field).

Example:

  1. requires = IS_STRONG(min=10, special=2, upper=2)

where:

  • min is minimum length of the value
  • special is the minimum number of required special characters, by default special characters are any of the following ~!!@#$%^&*()_+-=?<>,.:;{}[]| (you can customize these using specials = '...')
  • upper is the minimum number of upper case characters

other accepected arguments are:

  • invalid for the list of forbidden characters, by default invalid=' "'
  • max for the maximum length of the value
  • lower for the minimum number of lower case characters
  • number for the minimum number of digit characters

Obviously you can provide an error_message as for any other validator, although IS_STRONG is clever enough to provide a clear message to describe the validation failure.

A special argument you can use is entropy, that is a minimum value for the complexity of the value to accept (a number), experiment this with:

  1. >>> IS_STRONG(entropy=100.0)('hello')
  2. ('hello', Entropy (24.53) less than required (100.0))

Notice that if the argument entropy is not given then IS_STRONG implicitly sets the following defaults: min = 8, upper = 1, lower = 1, number = 1, special = 1 which otherwise are all sets to None.

CRYPT

This is also a filter. It performs a secure hash on the input and it is used to prevent passwords from being passed in the clear to the database.

  1. requires = CRYPT()

By default, CRYPT uses 1000 iterations of the pbkdf2 algorithm combined with SHA512 to produce a 20-byte-long hash. Older versions of web2py used md5 or HMAC+SHA512 depending on whether a key was specified or not.

If a key is specified, CRYPT uses the HMAC algorithm. The key may contain a prefix that determines the algorithm to use with HMAC, for example SHA512:

  1. requires = CRYPT(key='sha512:thisisthekey')

This is the recommended syntax. The key must be a unique string associated with the database used. The key can never be changed. If you lose the key, the previously hashed values become useless.

By default, CRYPT uses random salt, such that each result is different. To use a constant salt value, specify its value:

  1. requires = CRYPT(salt='mysaltvalue')

Or, to use no salt:

  1. requires = CRYPT(salt=False)

The CRYPT validator hashes its input, and this makes it somewhat special. If you need to validate a password field before it is hashed, you can use CRYPT in a list of validators, but must make sure it is the last of the list, so that it is called last. For example:

  1. requires = [IS_STRONG(), CRYPT(key='sha512:thisisthekey')]

CRYPT also takes a min_length argument, which defaults to zero.

The resulting hash takes the form alg$salt$hash, where alg is the hash algorithm used, salt is the salt string (which can be empty), and hash is the algorithm’s output. Consequently, the hash is self-identifying, allowing, for example, the algorithm to be changed without invalidating previous hashes. The key, however, must remain the same.

Special type validators

IS_LIST_OF

This validator helps you to ensure length limits on values of type list, for this purpose use its minimum, maximum, and error_message arguments, for example:

  1. requires = IS_LIST_OF(minimum=2)

A list value may comes from a form containing multiple fields with the same name or a multiple selection box. Note that this validator automatically converts a non-list value into a single valued list:

  1. >>> IS_LIST_OF()('hello')
  2. (['hello'], None)

If the first argument of IS_LIST_OF is another validator, then it applies the other validator to each element of the list. A typical usage is validation of a list: type field, for example:

  1. Field('emails', 'list:string', requires=IS_LIST_OF(IS_EMAIL()), ...)
IS_LIST_OF_EMAILS

This validator is specifically designed to work with the following field:

  1. Field('emails', 'list:string',
  2. widget=SQLFORM.widgets.text.widget,
  3. requires=IS_LIST_OF_EMAILS(),
  4. filter_in=lambda l: \
  5. IS_LIST_OF_EMAILS.split_emails.findall(l[0]) if l else l,
  6. represent=lambda v, r: \
  7. XML(', '.join([A(x, _href='mailto:'+x).xml() for x in (v or [])]))
  8. )

Notice that due to the widget customization this field will be rendered by a textarea in SQLFORMs (see next Widgets section). This let you insert and edit multiple emails in a single input field (very much like normal mail client programs do), separating each email address with ,, ;, and blanks (space, newline, and tab characters). As a conseguence now we need a validator which is able to operate on a single value input and a way to split the validated value into a list to be next processed by DAL, these are what the requires and filter_in arguments stand for. As alternative to filter_in, you can pass the following function to the onvalidation argument of form accepts, process, or validate method (see onvalidation section):

  1. def emails_onvalidation(form):
  2. form.vars.emails = IS_LIST_OF_EMAILS.split_emails.findall(form.vars.emails)

The effect of the represent argument (at lines 6 and 7) is to add a “mailto:…” link to each email address when the record is rendered in HTML pages.

ANY_OF

This validator takes a list of validators and accepts a value if any of the validators in the list does (i.e. it acts like a logical OR with respect to given validators).

  1. requires = ANY_OF([IS_ALPHANUMERIC(), IS_EMAIL()])

When none of the validators accepts the value you get the error message form the last attempted one (the last in the list), you can customize the error message as usual:

  1. >>> ANY_OF([IS_ALPHANUMERIC(), IS_EMAIL()])('@ab.co')
  2. ('@ab.co', 'Enter a valid email address')
  3. >>> ANY_OF([IS_ALPHANUMERIC(), IS_EMAIL()],
  4. ... error_message='Enter login or email')('@ab.co')
  5. ('@ab.co', 'Enter login or email')
IS_IMAGE

This validator checks if a file uploaded through the file input was saved in one of the selected image formats and has dimensions (width and height) within given limits.

It does not check for maximum file size (use IS_LENGTH for that). It returns a validation failure if no data was uploaded. It supports the file formats BMP, GIF, JPEG, PNG, and it does not require the Python Imaging Library.

Code parts taken from ref.[source1]

It takes the following arguments:

  • extensions: iterable containing allowed image file extensions in lowercase
  • maxsize: iterable containing maximum width and height of the image
  • minsize: iterable containing minimum width and height of the image

Use (-1, -1) as minsize to bypass the image-size check.

Here are some Examples:

  • Check if uploaded file is in any of supported image formats:
  1. requires = IS_IMAGE()
  • Check if uploaded file is either JPEG or PNG:
  1. requires = IS_IMAGE(extensions=('jpeg', 'png'))
  • Check if uploaded file is PNG with maximum size of 200x200 pixels:
  1. requires = IS_IMAGE(extensions=('png'), maxsize=(200, 200))
  • Note: on displaying an edit form for a table including requires = IS_IMAGE(), a delete checkbox will NOT appear because to delete the file would cause the validation to fail. To display the delete checkbox use this validation:
  1. requires = IS_EMPTY_OR(IS_IMAGE())
IS_FILE

Checks if name and extension of file uploaded through file input matches given criteria.

Does *not* ensure the file type in any way. Returns validation failure if no data was uploaded.

Its arguments are:

  • filename: string/compiled regex or a list of strings/regex of valid filenames
  • extension: string/compiled regex or a list of strings/regex of valid extensions
  • lastdot: which dot should be used as a filename / extension separator: True indicates last dot (e.g., “file.tar.gz” will be broken in “file.tar” + “gz”) while False means first dot (e.g., “file.tar.gz” will be broken into “file” + “tar.gz”).
  • case: 0 means keep the case; 1 means transform the string into lowercase (default); 2 means transform the string into uppercase.

If there is no dot present, extension checks will be done against empty string and filename checks against whole value.

Examples: Check if file has a pdf extension (case insensitive):

  1. INPUT(_type='file', _name='name',
  2. requires=IS_FILE(extension='pdf'))

Check if file is called ‘thumbnail’ and has a jpg or png extension (case insensitive):

  1. INPUT(_type='file', _name='name',
  2. requires=IS_FILE(filename='thumbnail',
  3. extension=['jpg', 'png']))

Check if file has a tar.gz extension and name starting with backup:

  1. INPUT(_type='file', _name='name',
  2. requires=IS_FILE(filename=re.compile('backup.*'),
  3. extension='tar.gz', lastdot=False))

Check if file has no extension and name matching README (case sensitive):

  1. INPUT(_type='file', _name='name',
  2. requires=IS_FILE(filename='README',
  3. extension='', case=0)
IS_UPLOAD_FILENAME

This is the older implementation for checking files, included for backwards compatibility. For new applications, use IS_FILE().

This validator checks if the name and extension of a file uploaded through the file input matches the given criteria.

It does not ensure the file type in any way. Returns validation failure if no data was uploaded.

Its arguments are:

  • filename: filename (before dot) regex.
  • extension: extension (after dot) regex.
  • lastdot: which dot should be used as a filename / extension separator: True indicates last dot (e.g., “file.tar.gz” will be broken in “file.tar” + “gz”) while False means first dot (e.g., “file.tar.gz” will be broken into “file” + “tar.gz”).
  • case: 0 means keep the case; 1 means transform the string into lowercase (default); 2 means transform the string into uppercase.

If there is no dot present, extension checks will be done against an empty string and filename checks will be done against the whole value.

Examples:

Check if file has a pdf extension (case insensitive):

  1. requires = IS_UPLOAD_FILENAME(extension='pdf')

Check if file has a tar.gz extension and name starting with backup:

  1. requires = IS_UPLOAD_FILENAME(filename='backup.*', extension='tar.gz', lastdot=False)

Check if file has no extension and name matching README (case sensitive):

  1. requires = IS_UPLOAD_FILENAME(filename='^README$', extension='^$', case=0)
IS_IPV4

This validator checks if a field’s value is an IP version 4 address in decimal form. Can be set to force addresses from a certain range.

IPv4 regex taken from ref.[regexlib]

The signature for the IS_IPV4 constructor is the following:

  1. IS_IPV4(minip='0.0.0.0', maxip='255.255.255.255', invert=False,
  2. is_localhost=None, is_private=None, is_automatic=None,
  3. error_message='Enter valid IPv4 address')

Where:

  • minip is the lowest allowed address
  • maxip is the highest allowed address
  • invert is a flag to invert allowed address range, i.e. if set to True allows addresses only from outside of given range; note that range boundaries are not matched this way

You can pass an IP address either as a string (e.g. ‘192.168.0.1’) or as a list or tuple of 4 integers (e.g. [192, 168, 0, 1]).

To check for multiple address ranges pass to minip and maxip a list or tuple of boundary addresses, for example to allow only addresses between ‘192.168.20.10’ and ‘192.168.20.19’ or between ‘192.168.30.100’ and ‘192.168.30.199’ use:

  1. requires = IS_IPV4(minip=('192.168.20.10', '192.168.30.100'),
  2. maxip=('192.168.20.19', '192.168.30.199'))

Notice that only a range for which both lower and upper limits are set is configured, that is the number of configured ranges is determined by the shorter of the iterables passed to minip and maxip.

The arguments is_localhost, is_private, and is_automatic accept the following values:

  • None to ignore the option
  • True to force the option
  • False to forbid the option

The option meanings are:

  • is_localhost: match localhost address (127.0.0.1)
  • is_private: match address in 172.16.0.0 - 172.31.255.255 and 192.168.0.0 - 192.168.255.255 ranges
  • is_automatic: match address in 169.254.0.0 - 169.254.255.255 range

Examples:

Check for valid IPv4 address:

  1. requires = IS_IPV4()

Check for valid private network IPv4 address:

  1. requires = IS_IPV4(minip='192.168.0.1', maxip='192.168.255.255')
IS_IPV6

This validator checks if a field’s value is an IP version 6 address.

The signature for the IS_IPV6 constructor is the following:

  1. IS_IPV6(is_private=None,
  2. is_link_local=None,
  3. is_reserved=None,
  4. is_multicast=None,
  5. is_routeable=None,
  6. is_6to4=None,
  7. is_teredo=None,
  8. subnets=None,
  9. error_message='Enter valid IPv6 address')

The arguments is_private, is_link_local, is_reserved, is_multicast, is_routeable, is_6to4, and is_teredo accept the following values:

  • None to ignore the option
  • True to force the option
  • False to forbid the option, this does not work for is_routeable

The option meanings are:

  • is_private: match an address allocated for private networks
  • is_link_local: match an address reserved for link-local (i.e. in fe80::/10 range), this is a private network too (also matched by is_private above)
  • is_reserved: match an address otherwise IETF reserved
  • is_multicast: match an address reserved for multicast use (i.e. in ff00::/8 range)
  • is_6to4: match an address that appear to contain a 6to4 embedded address (i.e. in 2002::/16 range)
  • is_teredo: match a teredo address (i.e. in 2001::/32 range)

Forcing is_routeable (setting to True) is a shortcut to forbid (setting to False) is_private, is_reserved, and is_multicast all.

Use the subnets argument to pass a subnet or list of subnets to check for address membership, this way an address must be a subnet member to validate.

Examples:

Check for valid IPv6 address:

  1. requires = IS_IPV6()

Check for valid private network IPv6 address:

  1. requires = IS_IPV6(is_link_local=True)

Check for valid IPv6 address in subnet:

  1. requires = IS_IPV6(subnets='fb00::/8')
IS_IPADDRESS

This validator checks if a field’s value is an IP address (either version 4 or version 6). Can be set to force addresses from within a specific range. Checks are done using the appropriate IS_IPV4 or IS_IPV6 validator.

The signature for the IS_IPADDRESS constructor is the following:

  1. IS_IPADDRESS(minip='0.0.0.0', maxip='255.255.255.255', invert=False,
  2. is_localhost=None, is_private=None, is_automatic=None,
  3. is_ipv4=None,
  4. is_link_local=None, is_reserved=None, is_multicast=None,
  5. is_routeable=None, is_6to4=None, is_teredo=None,
  6. subnets=None, is_ipv6=None,
  7. error_message='Enter valid IP address')

With respect to IS_IPV4 and IS_IPV6 validators the only added arguments are:

  • is_ipv4, set to True to force version 4 or set to False to forbid version 4
  • is_ipv6, set to True to force version 6 or set to False to forbid version 6

Refer to IS_IPV4 and IS_IPV6 validators for the meaning of other arguments.

Examples:

Check for valid IP address (both IPv4 and IPv6):

  1. requires = IS_IPADDRESS()

Check for valid IP address (IPv6 only):

  1. requires = IS_IPADDRESS(is_ipv6=True)

Other validators

CLEANUP

This is a filter. It never fails. By default it just removes all characters whose decimal ASCII codes are not in the list [10, 13, 32-127]. It always perform an initial strip (i.e. heading and trailing blank characters removal) on the value.

  1. requires = CLEANUP()

You can pass a regular expression to decide what has to be removed, for example to clear all non-digit characters use:

  1. >>> CLEANUP('[^\d]')('Hello 123 world 456')
  2. ('123456', None)

Database validators

IS_NOT_IN_DB
Synopsis:

IS_NOT_IN_DB(db|set, 'table.field')

Consider the following example:

  1. db.define_table('person', Field('name'))
  2. db.person.name.requires = IS_NOT_IN_DB(db, 'person.name')

It requires that when you insert a new person, his/her name is not already in the database, db, in the field person.name.

A set can be used instead of db.

As with all other validators this requirement is enforced at the form processing level, not at the database level. This means that there is a small probability that, if two visitors try to concurrently insert records with the same person.name, this results in a race condition and both records are accepted. It is therefore safer to also inform the database that this field should have a unique value:

  1. db.define_table('person', Field('name', unique=True))
  2. db.person.name.requires = IS_NOT_IN_DB(db, 'person.name')

Now if a race condition occurs, the database raises an OperationalError and one of the two inserts is rejected.

The first argument of IS_NOT_IN_DB can be a database connection or a Set. In the latter case, you would be checking only the set defined by the Set.

A complete argument list for IS_NOT_IN_DB() is as follows:

  1. IS_NOT_IN_DB(dbset, field, error_message='value already in database or empty',
  2. allowed_override=[], ignore_common_filters=True)

The following code, for example, does not allow registration of two persons with the same name within 10 days of each other:

  1. import datetime
  2. now = datetime.datetime.today()
  3. db.define_table('person',
  4. Field('name'),
  5. Field('registration_stamp', 'datetime', default=now))
  6. recent = db(db.person.registration_stamp > now-datetime.timedelta(10))
  7. db.person.name.requires = IS_NOT_IN_DB(recent, 'person.name')
IS_IN_DB
Synopsis:

IS_IN_DB(db|set, 'table.value_field', '%(representing_field)s', zero='choose one')

where the third and fourth arguments are optional.

multiple= is also possible if the field type is a list. The default is False. It can be set to True or to a tuple (min, max) to restrict the number of values selected. So multiple=(1, 10) enforces at least one and at most ten selections.

Other optional arguments are discussed below.

Example

Consider the following tables and requirement:

  1. db.define_table('person', Field('name', unique=True))
  2. db.define_table('dog', Field('name'), Field('owner', db.person))
  3. db.dog.owner.requires = IS_IN_DB(db, 'person.id', '%(name)s',
  4. zero=T('choose one'))

the IS_IN_DB requirement could also be written to use a Set instead of db

  1. db.dog.owner.requires = IS_IN_DB(db(db.person.id > 10), 'person.id', '%(name)s',
  2. zero=T('choose one'))

It is enforced at the level of dog INSERT/UPDATE/DELETE forms. This example requires that a dog.owner be a valid id in the field person.id in the database db. Because of this validator, the dog.owner field is represented as a drop-down list. The third argument of the validator is a string that describes the elements in the drop-down list, this is passed to the label argument of the validator. In the example you want to see the person %(name)s instead of the person %(id)s. %(...)s is replaced by the value of the field in brackets for each record. Other accepted values for the label are a Field instance (e.g. you could use db.person.name instead of ‘%(name)s’) or even a callable that takes a row and returns the description for the option.

The zero option works very much like for the IS_IN_SET validator.

Other optional arguments accepted by IS_IN_DB are: orderby, groupby, distinct, cache, and left, these are passed to the db select (see on Chapter 6 for their meanings).

Notice that groupby, distinct, and left do not apply to Google App Engine.

To alphabetically sort the options listed in the drop-down list you can set the sort argument to True (sorting is case-insensitive), this may be usefull when no orderby is feasible or practical.

The first argument of the validator can be a database connection or a DAL Set, as in IS_NOT_IN_DB. This can be useful for example when wishing to limit the records in the drop-down list. In this example, we use IS_IN_DB in a controller to limit the records dynamically each time the controller is called:

  1. def index():
  2. (...)
  3. query = (db.table.field == 'xyz') # in practice 'xyz' would be a variable
  4. db.table.field.requires = IS_IN_DB(db(query), ...)
  5. form = SQLFORM(...)
  6. if form.process().accepted: ...
  7. (...)

If you want the field validated, but you do not want a drop-down, you must put the validator in a list.

  1. db.dog.owner.requires = [IS_IN_DB(db, 'person.id', '%(name)s')]

Occasionally you want the drop-down (so you do not want to use the list syntax above) yet you want to use additional validators. For this purpose the IS_IN_DB validator takes an extra argument _and that can point to a list of other validators applied if the validated value passes the IS_IN_DB validation. For example to validate all dog owners in db that are not in a subset:

  1. subset = db(db.person.id > 100)
  2. db.dog.owner.requires = IS_IN_DB(db, 'person.id', '%(name)s',
  3. _and=IS_NOT_IN_DB(subset, 'person.id'))
IS_IN_DB and Tagging

The IS_IN_DB validator has an optional attribute multiple=False. If set to True multiple values can be stored in one field. This field should be of type list:reference as discussed in Chapter 6. An explicit example of tagging is discussed there. Multiple references are handled automatically in create and update forms, but they are transparent to the DAL. We strongly suggest using the jQuery multiselect plugin to render multiple fields.

Custom validators

All validators follow the prototype below:

  1. class sample_validator:
  2. def __init__(self, *a, error_message='error'):
  3. self.a = a
  4. self.e = error_message
  5. def __call__(self, value):
  6. if validate(value):
  7. return (parsed(value), None)
  8. return (value, self.e)
  9. def formatter(self, value):
  10. return format(value)

i.e., when called to validate a value, a validator returns a tuple (x, y). If y is None, then the value passed validation and x contains a parsed value. For example, if the validator requires the value to be an integer, x is converted to int(value). If the value did not pass validation, then x contains the input value and y contains an error message that explains the failed validation. This error message is used to report the error in forms that do not validate.

Notice that as alternative to custom validators, you can also use the onvalidation argument of form accepts, process, and validate methods (see onvalidation section).

The validator may also contain a formatter method. It must perform the opposite conversion to the one the __call__ does. For example, consider the source code for IS_DATE:

  1. class IS_DATE(object):
  2. def __init__(self, format='%Y-%m-%d', error_message='must be YYYY-MM-DD!'):
  3. self.format = format
  4. self.error_message = error_message
  5. def __call__(self, value):
  6. try:
  7. y, m, d, hh, mm, ss, t0, t1, t2 = time.strptime(value, str(self.format))
  8. value = datetime.date(y, m, d)
  9. return (value, None)
  10. except:
  11. return (value, self.error_message)
  12. def formatter(self, value):
  13. return value.strftime(str(self.format))

On success, the __call__ method reads a date string from the form and converts it into a datetime.date object using the format string specified in the constructor. The formatter method takes a datetime.date object and converts it to a string representation using the same format. The formatter is called automatically in forms, but you can also call it explicitly to convert objects into their proper representation. For example:

  1. >>> db = DAL()
  2. >>> db.define_table('atable',
  3. Field('birth', 'date', requires=IS_DATE('%m/%d/%Y')))
  4. >>> id = db.atable.insert(birth=datetime.date(2008, 1, 1))
  5. >>> row = db.atable[id]
  6. >>> print db.atable.birth.formatter(row.birth)
  7. 01/01/2008

Multiple Validators

Normally, when multiple validators are required (and stored in a list), they are executed in order and the output of one is passed as input to the next. The chain breaks when one of the validators fails.

Conversely, when we call the formatter method of a field, the formatters of the associated validators are also chained, but in reverse order.

As an alternative to the chained behavior described above, the ANY_OF validator can be used to combine a list of validators, and to pass if any of the validators pass (see the ANY_OF section for details).

Validators with dependencies

Usually validators are set once for all in models.

Occasionally, you need to validate a field and the validator depends on the value of another field. This can be done in various ways. It can be done in the model or in the controller.

For example, here is a page that generates a registration form that asks for username and password twice. None of the fields can be empty, and both passwords must match:

  1. def index():
  2. form = SQLFORM.factory(
  3. Field('username', requires=IS_NOT_EMPTY()),
  4. Field('password', requires=IS_NOT_EMPTY()),
  5. Field('password_again',
  6. requires=IS_EQUAL_TO(request.vars.password)))
  7. if form.process().accepted:
  8. pass # or take some action
  9. return dict(form=form)