Search code examples
pythonhtmlweb2pyweb2py-modules

Mimicking the behavior of HTML5 pattern attributes for inputs using web2py?


I'm looking to validate all of my forms with HTML5 and the pattern attribute, but as always, nothing is perfect on the internet and I still need a backup to catch those users who do not use HTML5 friendly browsers.

I'm having trouble finding exact representations of the regular expressions in the handbook so if you know, I'd appreciate some help. I have written the code in two ways, the HTML way and the HTML helper way (see below) and the HTML5 works and validates in modern browsers except IE as expected but I can't translate them to python.

An example, you will see below, is the expression pattern="[A-F0-9]{11}|[A-F0-9]{14}" This effectively forces an input to only contain uppercase letters of A-F and numbers 0-9. It also ensures the length is exactly 11 characters or 14. So how would I use this in web2py? IS_LENGTH seems to accept only a range from my tests and so far I've only found IS_ALPHANUMERIC to control what characters are accepted.

Please see the form below:

HTML:

<form id="activate_form" method="post" action="">
    <label for="meid">MSIE/ESN <sup>*</sup></label>
    <input name="meid" pattern="[A-Fa-f0-9]{11}|[A-Fa-f0-9]{14}" placeholder="MEID/ESN" required />
    <br />
    <label for="zip">Zip Code <sup>*</sup></label>
    <input type="number" name="zip" pattern="{5}" placeholder="Zip Code" required />
    <br />
    <br />
    <br /><br />
    <input type="button" name="cancel" value="Cancel" onClick="history.go(-1);return true;" />
    <input type="submit" name="submit" value="Activate" />
       <p class="small"><sup>*</sup> denotes a required field.
</form>

HTML Helpers:

form=FORM(LABEL('MEID/ESN ',(SPAN('*'))),INPUT(_name='meid',_pattern="[A-F0-9]{11}|[A-F0-9]{14}",_placeholder='MEID/ESN',_required='required',_title="The MEID/ESN number only contains 11 or 13 characters, the letters A-F, and the numbers 0-9.",requires=[IS_LENGTH(11|14),IS_NOT_EMPTY()], _onblur="this.checkValidity();"),BR(),LABEL('ZIP CODE ',(SPAN('*'))),INPUT(_name='zip',_type='number',_pattern="[0-9]{5}",_placeholder='Zip Code',_required='required',_title="We only required the five character zip code.",requires=IS_NOT_EMPTY()),BR(),BR(),BR(),BR(),INPUT(_type='button',_name='cancel',_value='Cancel',_onclick="history.go(-1);return true;"),INPUT(_type='submit',_name='submit',_value='Activate'),_method='post',_id='activate_form')
    if form.accepts(request,session):
        response.flash = 'Form accepted'
     #   redirect(URL('next'))
    elif form.errors:
        response.flash = 'Form has errors'
    return dict(form=form)

Solution

  • If you want to stick with client-side validation, you might also consider a Javascript polyfill library like Webshims Lib, which lets you use HTML5 features in older browsers. Also note that although client-side validation improves the user experience, it does not protect against malicious attacks, so you may still want some server-side validation in addition.

    Anyway, for server-side pattern validation, you can use the IS_MATCH() validator. For example:

    IS_MATCH('[A-F0-9]{11}|[A-F0-9]{14}', strict=True)
    

    That will match from the beginning of the string, and setting strict=True also requires matching the end of the string (equivalent to adding a "$" to the end of the regular expression). If you don't want to match the beginning of the string but instead search for the pattern anywhere in the string, you can set search=True.