Dispatcher

1 Introduction

Often, you want to leave a form but come back later with the already filled in values intact.

This happens, e.g., for form validation. When the validation fails, you will want to redisplay the form together with an appropriate error indication. An alternative here would be to put the validation code in the form itself and ensure, it is not activated for the first form entry (you could use a hidden variable for this purpose).

Another case are forms with complex input entries which require special support to be filled. When you leave the form to get this support, the form values provided so far must be saved in order to be available later, when you come back with the new value for the complex entry.

The dispatcher provides uniform support for these use cases. It controls a form and manages the form data for it. This includes initialization, saving data in a session object, dispatching to various destinations and restoring the saved values, when the form is reentered. Furthermore, it avoids problems with HTML checkbox and multiple selection handling.

2 Description

A form dispatcher is used to manage the variables of a form. It provides services for form initialization, form update and the dispatch out of the form to various form actions. During dispatch, the current form values are saved in a session object. When the form is updated, the data is put back into REQUEST.form and updated with the new values provided by update.

A form dispatcher works with (at least) the following objects:

Client

the id of the form controlled by the dispatcher

The dispatcher calls this object like a DTML method (or through mapply) after REQUEST.form and REQUEST.other have been set up.

The default is Client.

ClientDescription

the description of the managed form fields.

The dispatcher calls this object like a DTML method. The call should return either a list of form value descriptions or a dictionary thereof. Each description is a dictionary describing various aspects managed by the dispatcher.

The default is ClientDescription

SessionObject

the id of a session managing object.

This object is used to save the current form values during dispatch.

The default is FSSession.

SessionKey

the key to use for saving the current form values in the session object.

The default is the dispatchers physical path.

2.1 Use Cases

The form dispatcher supports the following use cases:

new

the form is used to create a new object.

The initial values of form variables are not taken from the environment unless the form description contains an explicit init or fixed description to this effect. Other form variables are empty (depending on dtype).

edit

the form is used to edit an existing object.

Most form variable values are taken from the environment. Only fixed values are taken from the description.

show

render with currently remembered values.

This method is used, when the Dispatcher is part or a larger context, e.g. a Wizzard. This context has performed global initialization or updates and then selectively calls for rendering of a dispatcher.

clear

the form values are reset to empty (depending on dtype). Only fixed values are not changed.

dispatch

the form is left with the intention to come back with new or changed values. The current form values are saved in the session object to be restored lated when we come back.

update

update the form after a dispatch.

The original form values are restored from the session object. They can be overridden or completed by additional values provided in the update call.

2.2 Event Sequences

The only anticipated event sequences are:

     (new | edit) (clear | show | dispatch update)* leave?

2.3 Properties

Client__

the id of the form controlled by the dispatcher

The default is Client.

ClientDescription__

the id of a method returning a dictionary or list that describes the form variables.

The default is ClientDescription.

Each variable is described by a dictionary. The following keys are interpreted. There may be more, used for other purposes:

name

the variable name

init

an initial value for new calls.

This is either None or a value. None means, use from the environment (if present). A missing init is equivalent to the type specific empty value.

fixed

the value is not changed by clear or __call__

This is either None or a value. None means, use from the environment (if present).

If fixed is not present, then clear resets the variable to the type specific empty value.

permanent

the value is not changed by update.

This should be a truth value, the default is 0.

It is usually used together with fixed variables to prevent modification by update.

It currently only affects update. It might be more intuitive, if permanent would imply fixed.

dtype

the variables data type (used to determine empty values)

Must be either string, int, list or tuple. Defaults to string.

SessionObject__

the id of the session object used to remember the values

The default is FSSession.

It is expected, that the session object has get and set methods to retrieve and set session data.

SessionKey__

the saved values are collected in a dictionary and saved under this key.

The default is the physical path of the dispatcher.

If the value is *dynamic*, then the session key is dynamically computed form the physical path. This is necessary, if the dispatcher is used as part of a ZClass.

initializeSession__

the id of a method that initialized the session.

The default is initializeSession.

FSSession requires that the session is initialized precisely once in each request that uses the session. Initializing more than once may lead to loss of data. Not initializing may lead to exceptions, let the application work with stale data and does not save modifications. It is expected that initializeSession is idempotent, i.e. can be called repeatedly in a single request without advertant effects.

For some session types (unlike FSSession), no explicit session initialization may be required. In such a case, initializeSession__ may be deleted.

2.4 Methods

2.4.1 new

fill a new form (mostly empty).

All variables get their initialization/fixed values (if defined). Other variables get the dtype specific empty value.

The values are placed in REQUEST and REQUEST.form.

None values for init or fixed are resolved from the environment.

The special value edit__=0 is defined.

The method is called either directly from the Web or from DTML, to better control the environment.

Arguments: DTML-like

2.4.2 edit

fill a form for editing (mostly filled).

All variables get their values from the environment with the exception of fixed variables. They get the specified value, unless the value is None.

The values are placed in REQUEST and REQUEST.form.

The special value edit__=1 is defined.

The method is usually called from DTML which sets up the environment.

Arguments: DTML-like

2.4.3 update

update the remembered values and render the client.

The variables are filled with their remembered values. New values from update override pervious values, unless errors__ has a true value. In this latter case, no values are overridden. In no case, update overrides a variable declared as permanent.

Usually, the update values are taken from the environment. If, however, values__ is defined, then its value must be a mapping and it, alone, is used for updating. The namespace is not relevant in this case.

The values are placed in REQUEST and REQUEST.form.

update restores the base URL, remembered during the last dispatch. This is in order to counter the lengthening URL problem that is favoured by Zopes acquisition.

The method is usually called from DTML. It may, however, also be called directly from the Web.

Arguments

update accepts DTML-like arguments. However, errors__ and values__ are treated specially.

If errors__ is defined and has a true value, the old values are simply restored. No values are overridden by the update.

If values__ is defined, then (with the exception of errors__), the environment is irrelevant. Only values__ (which must be a mapping) defines values to override remembered values. This provides for precise control over the overridden values.

2.4.4 __call__

remember the current values in the session object (with the exception of fixed variables) and dispatch to a new destination.

If the environment does not contain a value for a variable which is not fixed, then the empty value is used. This is necessary for checkboxes and multiple selections to work correctly.

The values and the base URL are saved in the session object for later restoration by update.

To determine the destination, __call__ looks for a variable of the form (.*)__disp(.[xy])?. It takes the part match by (.*) as the destination.

If the destination contains ?, it is split at it. The first part becomes the destination, the second part is decoded as a query string and the corresponding parameters placed into REQUEST and REQUEST.form.

The destination may be an URL path. restrictedTraverse is used to find the actual destination.

If no destination can be found, the client is simply rendered (equivalent to show). This is useful to have shorter URLs. Currently, the same happens, when REQUEST does not have a form attribute. However, this may change in the future.

The method is usually called only from the Web.

Arguments: dtml-like

2.4.5 clear

clear (make empty) non-fixed fields and render the client.

Arguments: DTML-like

2.4.6 show

render the client.

Arguments: DTML-like

2.4.7 getValues

return a mapping object containing the saved values.

This is the mapping saved in the session object. Writing to it changes the session maintained information.

Arguments: none

2.5 Arguments

Most methods support DTML-like arguments. This means, there are two optional positional arguments client and REQUEST and arbitrary keyword arguments. Unlike DTML-objects, the default value for client is the dispatcher object. REQUEST may be a mapping or a DTML namespace.

The methods construct a DTML namespace from this in the same way, DTML objects do. When an object is rendered, the render method of this namespace is used.

2.6 Rendering

Usually, new, edit, clear, and show render the client. If, however, DispatcherDoNotRender__ is true, rendering and REQUEST modification are suppressed. This allows e.g. a Wizzard to call these methods just to change the remembered values.

For a DTML object or when the object is not acquisition wrapped or when the namespace is bound, the namespace's render method is used for rendering. Other objects are rendered through ZPublisher.mapply.mapply.

Zopes acquisition and the possibility to call methods both via HTTP and directly can lead to great confusion with respect to base URLs. The base URL is essential for the resolution of any relative URL in an HTML page. The Dispatcher tries to counter this confusion by explicitly setting the HTML base element. For this purpose, it must find out its own URL. Note, that it can not use absolute_url, as this method discards any context. For new, edit, clear, show and __call__, the dispatcher assumes that it was activated directly by HTTP and that it can use REQUEST.URL1 or REQUEST.URL0, respectively. If this assumption is wrong, the caller can define DispatcherURL__. It should be the URL of the dispatcher object. If the client is rendered at the end of an update, the dispatcher restores the remembered base URL.

Note that the URL/BASE request variables are fixed for the rendering as well. They reflect the true client/destination information, not that of the dispatcher. Other request variables are not changed. This may change in the future.

3 Installation

Download the Dispatcher TGZ archive and unpack it into your Zope Products folder. Restart Zope. You should then find a Form Dispatcher entry in your add-list.

4 License

This product is covered by a Python-like open source license. For details, see the Copyright notice at the top of Dispatcher.py.

5 History

0.04
Zope 2.7 compatibility
0.03
Scripts which bind the namespace are now called via render. This allows to use Python Scripts with namespace binding to be used as client and to pass parameters to it via the namespace. Note, that this is not necessary in general, as all dispatcher variables go through REQUEST anyway.
0.02
Provisions for use in ZClasses
Extension of __call__ to behave like show, if no destination can be found.
mapply rendering is used for destinations which are not DTML or unwrapped objects. This allows to pass parameters to Python Scripts, even if they do not bind the namespace (which is really dangerous), and to External Methods. A future implementation should probably use the DTML render, if the target object uses namespace binding.

Dieter Maurer
Last modified: Fri Jan 2 20:43:33 CET 2004