wiki/webdev_study_part_4

Part 4: Data Parameters for Actions

Forms and Query Strings

Data provided by the user to invoke actions usually come in one of either two forms. The first is the query string that is part of the URL in a GET request. The second is the application/x-www-form-urlencoded data in the content-body of a POST request.

An example of encoded form data:

url=blah&title=test&tags=what&summary=test&submit=submit

Seems simple enough for a normal POST form but things quickly get tricky when items like multi-part file uploads join the party. This rules out just funneling all the data into urlparse.parse_qs() for all form management. Instead it would be better to use the venerable cgi.FieldStorage type to automatically manage the multiple content types like multipart/form-data and file uploads.

Validation

Usually platform libraries handle quite a bit of the leg work in decoding the submitted strings into usable values. But there is still quite a bit more that can be done to verify that the values submitted fit the preconditions of the actions defined in the web application. Making sure that the values fit the constraints of action parameters is the validation process. Whenever dealing with data from a source outside of the application a level of validation needs to be written in order to make it robust. This is especially true when dealing with user input and I'm sure the reader would agree.

Part of the validation process must be notification to the user of what the validation errors are if they occur. This means that form management must present functions that allow for recording the validation errors when they occur. How do these messages get stored, how are they reported and when is validation triggered?

Right now the garter.forms module does an intrinsic call to the validation procedure when the form object is created. If a validation error occurs then an exception is raised with the field errors embedded into the exception.

Another approach could be like the one Django uses where there is a is_valid() method off of the form object and side effects are used to dig out validation errors.

Whatever the means, field level and form level error reporting should be available to present back to the user.

Data Extraction

Data access from the facilities provided by the platform data decoders aren't exactly the easiest data structures to work with. This is only because those utilities have to keep an isomorphic mapping from the specifications defined by the standards to the resulting data structure after the decoding is done. But like most standards majority of the actual usage takes advantage of only a small subset of the features defined by the standard. Take for example the default data structure of the parse_qs() function which returns a dictionary of lists. So if you have a form that contains a search box query and a submit button you have to use

query = values['query'][0] # remember to index the first instance

As the application designer you know that you will only ever have one value for query yet you have to remember to subscript the data access for the first value in the list.

Presentation

Form presentation is the function of rendering some sort of user interface for population via user interaction.

Although it would be tempting to just rely on the type only for guidance in rendering the input component there are many times where elements of the same type require different input components based on UI guidelines, value restrictions, or even subjective preferences.

But the act of defining a definition of the input component on the form definition couples presentation with the application logic which is less than ideal. We can shirk this conundrum by simply leaving all presentation functions outside of the scope of the form definition and leave it strictly up to the designer. That approach while being pure in its separation of responsibilities is less than pragmatic. There are many instances where trivial form construction is required where programmatic approaches will outperform the hands on design work.

In order to do that, presentation logic can be decoupled from form definitions where unintrusive hints can be used to tell the presentation renderer how to present the elements defined in the form.

form = Form([
    ('title', FieldString()),
    ('url', FieldString()),
    ('tags', FieldString(presentation='my_tag_suggestion')),
    ('submit', FieldAction())
])

The above would define a form definition object for a url submission form. The title and url elements use a default string presentation tag (most likely 'edit') while the tags element would tell the presentation manager that this field should use a special customized tag suggestion presentation component (most likely AJAX style).

self.page_data['submit_form'] = form_table_render(form)

At this point it would be up to the form_table_render(form) to know the details of rendering "edit", "submit", and "my_tag_suggestion" input components.