The ValidatorConverters are one of the really neat ideas from FunFormKit (though I use dalchemys FormKit)
I found myself wanting to do some additional Validation of Query Strings and Session values and I wanted to re-use the ValidatorConverters that I had.
The code I'm going to present isn't pretty, maybe someone has done this better already and if so I'd prefer to use that but its a *technique* I think has some value even if my coding is under-par.
Even a simple webpage might take a query-string value like 'id'. You need to do several tests :
1. Make sure the query string contains "id".
2. Make sure "id" is numeric and if not, try to convert it.
3. Make sure "id" is in range.
4. If any of the above fail, redirect somewhere to say "Nice try buddy"
5. Put "id" somewhere useful so you don't have to refer to it as self.request.field('id') - maybe self.id would be nice?
In FormEncode (Sandbox/ianbicking/FormEncode/Schema.py) I handle this by validating the entire request dictionary. A form validator is a validator that has sub-validators, and collects the output from all of them. It works quite nicely, and keeps the interface small -- validating a request has the same interface as validating a single field. Using the old ValidatorConverter model it might look like (roughly):
class MyIDValidator(ValidatorConverter):
def convert(self, value):
value = value.copy()
if not value.has_key('id'): raise InvalidField, 'some message'
try:
value['id'] = int(value['id'])
except ValueError:
raise InvalidField, 'yada yada'
return valueOr in FormEncode:
class MyIDValidator(FormEncode.Schema.Schema):
id = FormEncode.Validator.Int()Neither of these puts the value someplace convenient. Another thing I've experimented with is a dictionary unpacker (Sandbox/ianbicking/FormEncode/DictCall.py). It reads your method signature and works from that, so you might do:
def someAction(self, id_int):
...You'd run this like DictCall.dictCall(self.someAction, self.request().fields()), though obviously some fiddling with Page would make this more elegant. If a value doesn't have a default (as id_int does not) then it is required. If you give **kw in the signature then that gets all the extra fields. You append _int, _float, _list, _set, and _dict for type conversion. _list and _dict do unpacking (e.g., "a:b=10&a:c=20" becomes {'a': {'b': 10, 'c': 20}}), and you can do some nesting like a_dict_int.
It's not really extensible on several levels -- like you can't validate that the integer is within a range, and you can't do other sorts of conversion. But it does pretty decent unpacking within its range of functionality. FormEncode/VariableDecode.py does a somewhat more robust but limited unpacking (unlike DictCall, it's meant to be used with FormEncode validators).
Maybe a more elegant solution would be something like:
class MyPage(FormEncodeAwarePage):
def someAction(self, id):
...
# The schema is implicitly associated with someAction by its name:
class someActionSchema(FormEncode.Schema.Schema):
id = FormEncode.Validator.Int()Then in FormEncodeAwarePage.respond it might handle actions like:
# You set this class variable in subclasses:
_defaultAction = None
# (Most pages would have a default action)
def respond(self, trans):
action = self._defaultAction
for name, value in trans.request().fields():
if name.startswith('_action_'):
if name == '_action_': action = value
else: action = name[len('_action_'):]
break
if action:
schema = getattr(self, action+'Schema', None)
if not schema: raise Forbidden
input = schema().toPython(self.request().fields())
getattr(self, action)(**input)So, just some ideas on the same line of thinking.
(All code in above email is untested, of course)
-- Ian Bicking | [EMAIL PROTECTED] | http://blog.ianbicking.org
------------------------------------------------------- This sf.net email is sponsored by:ThinkGeek Welcome to geek heaven. http://thinkgeek.com/sf _______________________________________________ Webware-discuss mailing list [EMAIL PROTECTED] https://lists.sourceforge.net/lists/listinfo/webware-discuss
