I’ve been doing a lot of work with validation in python recently, and wanted to point out a few nice things about validation in a python web app using FormEncode vs the client side validation in ColdFusion.
First, lets review the standard (built in) way to do form validation in ColdFusion
[html]
<cfinput ..... name="mydate" required="yes"
message="You must enter data in this field" validate="date">
[/html]
Simple enough right? but the are a few things to be learned from other languages.
FormEncode has a nice separation from the display and the validation. I use zope page templates so I end up with the following HTML (it’s really a zpt page)
[html]
<input .... name="mydate"> <form:error name=mydate" />
[/html]
Or you could also use a custom error message
[html]
<form:iferror name="mydate">...</form:iferror>
[/html]
FormEncode will replace the <form:error name="mydate" />
tag with a <span class="error-message">...</span>
when there is an error. This allows you to specify in your html code where in the page the error should be displayed. It also allows you to style it as appropriate. When there is an error on that field, the input box also gets a class="error"
appended to it (preserving the existing class names).
Thats the display. Specifying the validator happens is done by creating a “schema” for the form.
[python]
class Validator(Schema):
mydate = validators.DateConvertor(not_empty=True)
[/python]
There are of course a few other things which we would do to style some output. CSS is always nice.
[css]
input.error{border:1px solid #f00;border-color:#a00 #f66 #f66 #a00;color:#a00;}
span.error-message{padding:0 .5em 0 .5em;color:#fff;background-color: #f00;}
[/css]
You can make up your mind, but I think that kind of error message is easier. I left out a few things though, because there is more magic that needs to happen to get the <form:error>
tag to morph into the error message. FormEncode comes with htmlfill which processes the html page and fills it with the error message.
This is what a custom Fax number validate looks like, and how to use more than one validator.
[python]
class Validator(Schema)
FaxNumber = FaxValidator()
class FaxValidator(validators.FancyValidator):
messages={
'invalidalpha':'Fax number must not contain alphabetic characters',
'invalidchar':'Fax number cannot contain a %(part)s'
}
validNonNumeric = '() -+.'
def validate_python(self,value,state):
if not value:
return None
if len(value) == 0:
return None
numList = re.split("[,; :|]",value)
for num in numList:
if num == '' or num == ' ':
continue
digits = ''
# pull the fax number out of its formatting and check length of number
for part in num:
if part.isdigit():
digits += part
elif part.isalpha():
raise Invalid(self.message("invalidalpha",value),value,state)
elif part not in self.validNonNumeric:
raise Invalid(self.message("invalidchar",value,part=part),value,state)
return None
[/python]
And the following schema declaration chains validators together on the same field.
[python]
class Validator(Schema):
FaxNumber = All(FaxValidator(),validators.String(max=24,min=10))
[/python]
Python validation is nice, and customizable. CF should learn a few things.