[Zope-dev] Form error handling for Rack-stored PD classes in O-O application

Itai Tavor itai@optusnet.com.au
Wed, 4 Apr 2001 11:39:24 +1000


Hi,

I often have more luck the second time I ask a question... let's see 
if this rule holds.

I'm struggling with checking and reporting errors in forms that 
create or modify properties of PD classes in a ZPatterns application.

Automatic checks using Zope's built-in constraints or something like 
ValueHandler won't cut it, because they can't cover all the required 
rules - for example, checking if a Product SKU is being set to an 
existing SKU, or that a Customer is trying to use an email address 
already in use (and also because I want to return the form page with 
the errors indicated, rather than a generic error page). So I need a 
specific verification method for each class. This method will be used 
both when an object is edited and when its created. To use the method 
when creating an object, I either have to place it in the Specialist 
- which is bad, because the PD class should provide its validation 
code, or call it at commit time, using a SkinScript WHEN OBJECT 
CREATED,CHANGED clause. But verification methods called at commit 
time do their checks on the object properties, not on any submitted 
form field values. So they can test that a password is reasonably 
secure, but can't check that passwords typed in two form fields are 
identical.

One thing I can do is define a editInstance method which would verify 
the values in REQUEST.form and then call manage_changeProperties, or 
store a list of errors in the REQUEST and raise a 'FormError' 
exception. But I can't let this exception propagate freely, I have to 
catch it so I can display an appropriate error page. Also, if a form 
results in the creation of more than one object, I want to display 
all the errors that result from the creation of all the objects, but 
if the first object raises an exception, I can't create the second 
one (especially if the second one expects to get the id of the first).

Or I could forget about exceptions and simply return an error list 
from editInstance. Then it would be up to the method calling 
editInstance to raise an exception to roll back the created objects.

This is the only workable solution I got at the moment... I can get 
it to work, but I've chased solutions in the past that seemed good 
but ended up crashing... so I'd really appreciate any comments on 
possible flaws or problems I'm not seeing, or any other, better 
solutions.

This is what it would look like (just an example, not showing cases 
where more complex stuff happens, like 2 objects being created with a 
single form):

In Customer (DataSkin PD class):

     def editInstance(self, REQUEST, errors):
         err = self.verify(REQUEST)
         if err != {}:
             errors.update(err)
         else:
             self.manage_changeProperties(REQUEST)

     def editInstance_html(self, REQUEST, RESPONSE):
         """Action method for editInstanceForm_html"""
         errors = {}
         self.editInstance(REQUEST, errors)
         if errors != {}:
             html = self.editInstanceForm_html(self, REQUEST, errors=errors)
             raise 'FormError', html
         RESPONSE.redirect('editInstanceForm_html?message=Changes%20saved.')

     def verify(self, REQUEST):
         errors = {}
         if REQUEST['name'] == '':
             errors['name'] = 'Please provide a name'
         return errors

In CustomerManager (Specialist):

     addCustomer(self, REQUEST, errors):
         ni = self.defaultRack.newItem()
         ni.editInstance(REQUEST, errors)
         return ni

     addCustomer_html(self, REQUEST, RESPONSE):
         """Action method for addCustomerForm_html"""
         errors = {}
         ni = self.addCustomer(REQUEST, errors)
         if errors != {}:
             html = self.addCustomerForm_html(self, REQUEST, errors=errors)
             raise 'FormError', html
 
RESPONSE.redirect('manageInstances_html?message=New%20customer%20created.')

And, of course, I can also have WHEN OBJECT ADDED,CHANGED CALL 
self.ensure_valid(), to catch anything that might get by the above 
checks. ensure_valid will raise its own error page, independent of 
the above.


TIA for any help/comments

Itai
-- 
--
Itai Tavor                      -- "Je sautille, donc je suis."    --
itai@optusnet.com.au            --               - Kermit the Frog --
-- 'Supposing a tree fell down, Pooh, when we were underneath it?' --
-- 'Supposing it didn't,' said Pooh after careful thought.         --