views:

27

answers:

2

I'm building a form class in python for producing and validating HTML forms. Each field has an associated widget which defines how the field is rendered.

When the widget is created, it is passed in a (default) value so that it knows what to display the first time it is rendered. After the form is submitted, the widget is asked for a value. I delegate this to the widget rather than just nabbing it from the POST data because a widget may consist of several HTML inputs (think of a month/day/year selector). Only the widget knows how to mash these together into one value.

Problem is, I don't know if I should have the widget always accept a string, and always return a string for consistency, or accept and return a data type consistent with its purpose (i.e., a date selector should probably return a DateTime object).

The philosophy behind my form class is "mix and match". You choose what widget you want, and what validators/formatters/converters you want to run on it. Which I guess lends itself towards "use strings" and let the developer decide on the data type afterwords, but... I can't think of a good but. Do you anticipate any problems with this approach?

+1  A: 

This approach is quite generic and serializing to and from strings should always work fine. You could also save the state of a widget to a file or send it over a network for recreating another widget from it.

Some potential issues or aspects to consider:

  • Localization: how to interpret the string regarding the culture, which format is the canonical format for comparisons.
  • Performance: some transformations might be time consuming, but I assume for human interaction that will be far fast enough.
jdehaan
+1  A: 

While simply passing strings around seems like a useful idea, I think you're going to discover it doesn't work as well as you might hope.

Think about the date example—instead of passing around a date object, instead you pass around a str of the format "2010-01-01". In order to work with that data, every user of the class needs to know not only that it's a str which represents a date, but what the format of that string is. In other words, you haven't gained anything. Worse, you lose the ability to pass a datetime object into the widget (unless you take extra steps to deal with that case).

The validator or formatter issue isn't as big a deal as you might think; how often are you going to want to validate a string which doesn't represent a date as if it were a date?

Chris B.
So you're proposing that all date widgets should accept datetime objects? And I suppose then they should return datetime objects as well? Thus the validator... isn't really needed because the value is already in the correct format? You're right that you wouldn't want to represent a non-date-string as a date, but what about if the developer wants to use a plain text input widget for dates? Then the widget must return a string because it doesn't really know what's being entered, and then it *does* need to be properly validated. Of course, the developer could omit the validator in the former...
Mark
...case, but add it in the second case. But now we're producing inconsistencies which may complicate things further? What if the developer starts with a plain text widget for dates, and thus the default argument needs to be a string, and then decides to switch it with an actual date widget but forgets to update the default arg into a datetime object? I can see both sides... which is why this is a tough decision to make :(
Mark
This is Python, so the date widget should accept anything that looks like a date, and return a date. I'm assuming the widget would take the form inputs and create a valid date (or throw an error). If you wanted to use a plain text widget for dates, I'd just inherit from the regular text widget and add the magic that deals with dates there (front-end validation could be a part of it).
Chris B.
If the developer forgot to update the default arg, you'd get an exception the first time you ran the code. It's not going to be an issue.
Chris B.
I think this sounds like an acceptable solution... my biggest concern is having exceptions pop up all over the place due to treating variables as the incorrect data type. I'd rather have the error be a ValidationError that can be passed on to the UI rather than taking down the whole app. Wish there was a way to enforce variable type for function args. For example, should the `greaterthan` validator only accept ints (and expect you to cast it before hand), or attempt to cast to int itself? If the former, you wind up with exceptions, if the latter you wind up with more verbose code, and ...
Mark
... redundant checks.
Mark
You can easily make the error a `ValidationError`. Just wrap the whole function in a `try/catch` block, and repackage any unexpected exceptions with a `ValidationError` of your own design. You could even make that a decorator, and it would keep your code tidy. But I think you'll find that's unnecessary; these aren't the sort of errors that crop up in production--you determine what gets passed, and passing the wrong type is so rare (and so easy to detect and correct when you do) that you don't need particularly robust error handling.
Chris B.