tags:

views:

93

answers:

4

I'm trying to devise a scheme for validating form fields. I've decided you can pass in a list of validators to each field like,

Field(validators=[email_validator, required_validator])

But then I thought, what if you wanted to or the validators together, rather than anding them? For example, a field that accepts either a Canadian postal code, or a US zip code. Sure, I could just create a new validator that could accept either, but it would be neat if I could come up of a way of allowing the user to choose.

Is there maybe some way that I could manipulate the bitwise operators?

Field(validators = required&(postal_code|zip_code))

Or am I just making things way too complicated and shooting myself in the foot?

+1  A: 

What about a list whose items are either single validators, or list of validators to be anded together -- the top-level list does an or on all of them. You could do it the other way 'round of course, but normally "and binds tighter" by convention, so this seems acceptable.

More explicitly, you could use items that are the literal (quoted) strings 'and', 'or' (and possibly 'nand' and 'nor'!) as "operators" -- if the first item of a string (top-level or substring) is such an operator, it says what to do with all the others (the default could be 'and').

Alex Martelli
Right, and why stop here? Why not define this all in the XML? And just in case declarative programming leads to a dead end, why not invent a Lisp interpreter while at it? Only when all other options fail will we go back to writing plain old Python code.
Hamish Grubijan
+4  A: 

Django solves this by using Q objects which can be combined using & and | to create more complex conditions. Mimicing what they use is probably an acceptable solution.

Ignacio Vazquez-Abrams
Hm ... so, was I wrong about error messages?
Hamish Grubijan
@Hamish: Not necessarily, but each object could contribute to the error message. "Entry must: <ul><li>be between 8 and 15 characters</li><li>contain numbers and symbols</li></ul> OR <ul><li>be the name of an animal</li></ul>"
Ignacio Vazquez-Abrams
I'm leaning towards this answer... I think I have an idea of how to implement it too.
Mark
+1  A: 

You would want to implement __or__(self, other) and __and__(self, other) on your validator class, which dynamically returns a new validator that knows how to do the appropriate job. So, you'd basically want to implement your validators such that they can be dynamically combined in this manner (i.e., if they work with regular expressions, and you can make assumptions about how the regular expressions are written, you could combine the regular expressions appropriately in the new validator you return).

http://docs.python.org/reference/datamodel.html#emulating-numeric-types

Joseph Spiros
+1  A: 

sqlobject uses something like this, here's an example:

MyTable.select((MyTable.q.name == "bob") | (MyTable.q.age == 5))

The major problem with this approach is the precedence of operators. It requires many more brackets to do it nicely. A nicer approach is to use an And() and Or() function and use prefix notation instead of infix notation. This has the benefit that it's discoverable to a new programmer who is reading your code.

In your example above, I would suggest something like:

Field(validators=[required, Or(postal_code, zip_code)])

Where all the validators in this list are Anded together implicitly. This makes the trivial case easy (the trivial case meaning, "match all these validators") and then have quite complicated expressions possible, such as:

Field(validators=Or(And(IsAmerica(), zip_code), 
                     And(Not(IsAmerica()), city))])

Of course, this is just a suggestion. As demonstrated, using & and | work fine in some systems (Django, sqlobject). You can see clearly in my example that complicated cases have problems with many layers of brackets, and that's a tradeoff you should consider.

Jerub
How do you produce a meaningful error message automatically? What could you possibly need other than `or` and `and`?
Hamish Grubijan