Spike

drfloob.com

About

My name is AJ, and I'm a web developer and software engineer out of Southern California.

If you're interested in hiring me, take a look at my portfolio and send me an email

A Hidden Gem in FormEncode, and a Plate Full of Beef

While learning Pylons, I found myself digging into the documentation of a few different tools that come packaged with it. FormEncode has so far been the most intriguing. I ended up digging into the code, and found a hidden gem. I've also got a few improvement ideas to toss around, in the form of oh-so-tender beef.

formencode.compound.Pipe

Haven't heard of it, have you? Don't be surprised, it's not listed anywhere in the documentation. The Pipe compound validator allows you to chain validators together, so the output of one flows into the input of the next. It's a lot like formencode.compound.All, except Pipe guarantees you a specific order of validator execution. This is only useful if you're doing conversions in your validators, I suppose. But that's exactly what I'm doing.

I found Pipe because I was going to write it myself. I started digging through FormEncode's source to see if I could hijack All's functionality, and there it was, my work already finished for me.

I left a message on the formencode mailing list, letting Ian know that the documentation needs to be updated, but nobody has responded yet. In the meantime, the idea is simple and self-explanatory, and the source code is readable. Dig in!

Beef 1: is this some wacky edge-case or what?

I don't think I'm doing anything unusual with my html forms or form processing logic. But I keep finding my needs neglected by FormEncode's defaults.

To begin with, I would expect to be able to validate an entire form before sending it back to the user. If I'm giving a user a detailed form with 20 required fields (a ridiculous example for today's common attention spans, I realize), and that user screws up every single field, I'd like to let them know everything they did wrong in one shot rather than have them resubmit 20 times!

The only solution I've found is to place all my validators inside my Schema's chained_validator attribute, and for that to work, you must enable validate_partial_form. It's a bit ugly, but I can live with that. But the implications make this solution completely ridiculous. From FormEncode's Validation introduction:

If you validate a partial form you should be careful that you handle missing keys and other possibly-invalid values gracefully.

I see. So, if I'd like to run all my validators before returning a set of errors, I have lost FormEncode's default error detection, and have to reinvent it myself. Excuse me, WTF? I must be the only person who wants to validate an entire form at once.

Beef 2: shot down for trying to be helpful

How could a form be more helpful to a user? Let's pretend we're using Lynx for a second, and rule out any new-age ajaxy goodness (I always add that last).

Well, if any submitted field could use some cleaning up, I think it's the least my application could do to return a tidied version of the submitted data. For example, if a form field accepted HTML, you might want to return their code with proper indentation and a doctype. Or, for a phone number field, it could be returned with a parenthesized area code and hyphenated local number.

I want FormEncode to be the tool that accomplishes that. In my mind, that's where form-submission filtering and conversion should belong. Unfortunately, from everything I understand about FormEncode, this is not readily possible. It hinges on the ideas that a validator could both fail and store a cleaned version of the submitted data. Maybe I haven't looked hard enough, or hacked a solution creative enough to do it, but I don't think FormEncode's design permits anything like this.

Final thoughts

I do like FormEncode. I wouldn't have dug in so hard if I didn't. I think the project has a lot of promise, and maybe someday I'll either contribute some changes back or fork my own. For now, I'll suffice with using an otherwise awesome tool on my most beloved project, and hope I finish before the idea becomes irrelevant.