[Checkins] SVN: zope3org/trunk/src/zorg/multiform/ refactored

Bernd Dorn bernd.dorn at fhv.at
Sun Apr 9 05:05:26 EDT 2006


Log message for revision 66708:
  refactored

Changed:
  U   zope3org/trunk/src/zorg/multiform/README.txt
  U   zope3org/trunk/src/zorg/multiform/multiform.py

-=-
Modified: zope3org/trunk/src/zorg/multiform/README.txt
===================================================================
--- zope3org/trunk/src/zorg/multiform/README.txt	2006-04-09 08:55:00 UTC (rev 66707)
+++ zope3org/trunk/src/zorg/multiform/README.txt	2006-04-09 09:05:26 UTC (rev 66708)
@@ -22,10 +22,12 @@
 
 Let us create a new multiform class which should display IOrder objects.
 
-    >>> from multiform.multiform import MultiForm,ItemFormBase
+    >>> from multiform.multiform import MultiFormBase,ItemFormBase
     >>> from zope.formlib import form
 
     >>> class OrderForm(ItemFormBase):
+    ...     form_fields = form.Fields(IOrder,omit_readonly=False,
+    ...     render_context=True)
     ...     def __call__(self, ignore_request=False):
     ...         widgets = form.setUpWidgets(
     ...             self.form_fields, self.prefix, self.context, self.request,
@@ -34,37 +36,124 @@
     ...     widgets])
 
 
-    >>> class OrdersForm(MultiForm):
+    >>> class OrdersForm(MultiFormBase):
     ...     
-    ...     form_fields = form.Fields(IOrder)
-    ...     
+    ...     itemFormFactory = OrderForm
     ...     def __call__(self, ignore_request=False):
-    ...         self.setUpForms()
+    ...         self.setUpWidgets()
     ...         res = u''
-    ...         for form in self.forms.values():
-    ...             res += '<div>%s</div>\n' % form(ignore_request=True)
+    ...         names = sorted(self.subForms.keys())
+    ...         for name in names:
+    ...             res += '<div>%s</div>\n' % self.subForms[name](
+    ...             ignore_request=ignore_request)
     ...         return res
     ...     
-    ...     def itemForm(self, item, **kwargs):
-    ...         return OrderForm(item, self, **kwargs)
 
-
-
     >>> from zope.publisher.browser import TestRequest
     >>> request = TestRequest()
     >>> view = OrdersForm(orderMapping, request)
     >>> print view()
     <div>
-    <div>1</div><div>n1</div>
+    <div>0</div><div><input ... name="form.0.name" ... value="n0" ...</div>
     </div>
     <div>
     ...
+    </div>
+
+If the request contains any form data, that will be reflected in the
+output:
+
+    >>> request.form['form.1.name'] = u'bob'
+    >>> print OrdersForm(orderMapping,request)()
     <div>
-    <div>4</div><div>n4</div>
+    ...
+    <div>1</div><div><input ... name="form.1.name" ... value="bob" ...</div>
+    ...
     </div>
 
+Sometimes we don't want this behavior: we want to ignore the request values,
+particularly after a form has been processed and before it is drawn again.
+This can be accomplished with the 'ignore_request' argument in
+setUpWidgets.
 
+    >>> print OrdersForm(orderMapping, request)(ignore_request=True)
+    <div>
+    ...
+    <div>1</div><div><input ... name="form.1.name" ... value="n1" ...</div>
+    ...
+    </div>
 
+In order to define a save action on the multiform we have to define a
+parent action on our OrderForm. A parent action is rendered only one
+time in the parent multiform, but applied to all subforms.
+
+    >>> from multiform import multiform
+    >>> class OrderForm2(ItemFormBase):
+    ...     form_fields = form.Fields(IOrder,omit_readonly=False,
+    ...     render_context=True)
+    ...     def update(self):
+    ...         super(OrderForm2,self).update()
+    ...         
+    ...     @multiform.parentAction(u"Save")
+    ...     def handle_save_action(self, action, data):
+    ...         form.applyChanges(self.context, self.form_fields,
+    ...         data, self.adapters)
+    ...     def render(self):
+    ...         return '\n<div>%s</div>\n' % '</div><div>'.join([w() for w in
+    ...     self.widgets])
+
+
+Now we have to set the factory on the OrdersForm.
+
+    >>> class OrdersForm2(OrdersForm):
+    ...     itemFormFactory=OrderForm2
+    ...     def __call__(self, ignore_request=False):
+    ...         self.setUpWidgets()
+    ...         res = u''
+    ...         names = sorted(self.subForms.keys())
+    ...         for name in names:
+    ...             res += '<div>%s</div>\n' % self.subForms[name]()
+    ...         return res
+
+
+
+    >>> pf = OrdersForm2(orderMapping,request)
+    >>> pf.setUpWidgets()
+    >>> action = [action for action in pf.subForms['1'].actions][0]
+    >>> action
+    <multiform.multiform.ParentAction object at ...>
+
+The name of the action is without the item key, because it is applied
+ to all items.
+
+    >>> print action.__name__
+    actions.save
+
+    >>> request = TestRequest()
+    >>> orderMapping = dict([(str(k),Order(k,name='n%s'%k)) for k in range(2)])
+    >>> request.form[pf.prefix + '.' + action.__name__]=u''
+    >>> print OrdersForm2(orderMapping,request)()
+    Traceback (most recent call last):
+    ...
+    FormError: ('No input', 'name')
+
+Ups, we have an error because we didn't provide the input data on the
+request. The form requires to have all input fields in the request if
+an action should be supplied.
+
+Let's supply request Data.
+
+    >>> for i in range(2):
+    ...     request.form['form.%s.name' % i]='new name %s' % i
+    ...     request.form['form.%s.identifier' % i]= i
+    >>> print OrdersForm2(orderMapping,request)()
+    <div>
+    <div... value="new name 0" ...
+    <div... value="new name 1" ...
+    </div>
+
+
+
 TODO:
 
 - encode prefix, do we have to do it?

Modified: zope3org/trunk/src/zorg/multiform/multiform.py
===================================================================
--- zope3org/trunk/src/zorg/multiform/multiform.py	2006-04-09 08:55:00 UTC (rev 66707)
+++ zope3org/trunk/src/zorg/multiform/multiform.py	2006-04-09 09:05:26 UTC (rev 66708)
@@ -5,292 +5,77 @@
 from zope.app.form.browser.interfaces import IWidgetInputErrorView
 
 from zope.formlib import form
+from zope.formlib.interfaces import IBoundAction
 from zope.formlib.i18n import _
 from interfaces import IMultiForm
+from zope import interface
 
-def availableActions(form, actions):
-    result = []
-    for action in actions:
-        if not action.label in form.table.config.actions:
-            continue
-        condition = action.condition
-        if condition is not None:
-            if not condition(form, action):
-                continue
-        result.append(action)
-    return result
+class ParentAction(form.Action):
 
+    """an action that is rendered in the parent multiform object and
+    is applied to all subForms"""
 
-class TableAction(form.Action):
+    def __get__(self, form, class_=None):
+        if form is None:
+            return self
+        result = self.__class__.__new__(self.__class__)
+        result.__dict__.update(self.__dict__)
+        result.form = form
+        #result.__name__ = form.prefix + '.' + result.__name__
+        interface.alsoProvides(result, IBoundAction)
+        return result
 
-    def render(self):
-#        if self.label in self.form.table.config.actions and \
-#           not self.form.table.config.actions[self.label].isLocal:
-#            return super(TableAction,self).render()
-#        else:
-#            return ""
-        return super(TableAction,self).render()
+    def submitted(self):
+        # override to find the matching prefix
+        if not self.available():
+            return False
+        form = self.form.parentForm
+        name = "%s.%s" % (form.prefix, self.__name__)
+        return name in form.request.form
 
 
-class RowAction(form.Action):
+class parentAction(form.action):
 
-    def render(self):
-#        if self.label in self.form.table.config.actions and \
-#           self.form.table.config.actions[self.label].isLocal:
-#            return super(RowAction,self).render()
-#        else:
-#            return ""
-        return super(RowAction,self).render()
-
-
-class rowAction(form.action):
-    
     def __call__(self, success):
-        action = RowAction(self.label, success=success, **self.options)
+        action = ParentAction(self.label, success=success, **self.options)
         self.actions.append(action)
         return action
 
 
-class tableAction(form.action):
-    
-    def __call__(self, success):
-        action = TableAction(self.label, success=success, **self.options)
-        self.actions.append(action)
-        return action
-
-
-def isRowEditMode(form, action):
-    return form.mode == 'edit' and action.label in form.table.config.actions and form.row.selected
-
-
-def isRowDisplayMode(form, action):
-    return form.mode == 'display' and action.label in form.table.config.actions
-
-
 class ItemFormBase(form.FormBase):
 
-    newmode = None
+    parentForm = None
 
-    def __init__(self, item, multiForm, **kwargs):
-        super(ItemFormBase,self).__init__(item,
-                                         multiForm.request)
-        self.multiForm = multiForm
-        self.baseRow_actions = form.Actions()
+    def __init__(self,context,request,parentForm):
+        super(ItemFormBase,self).__init__(context,request)
+        self.parentForm=parentForm
 
-        # build up form_fields
-        # kwargs includes form relevant parameters
-        self.form_fields = form.Fields()
-        for field in self.multiForm.form_fields:
-            isDisplay = not(self.multiForm.mode == 'edit' and self.isSelected())
-            fieldkwargs = {}
-            fieldkwargs['for_display'] = isDisplay
-            # XXX why is render_context not set?
-            fieldkwargs['render_context'] = True
-            if not isDisplay and field.widget is not None:
-                fieldkwargs['custom_widget'] = field.widget
-            self.form_fields = self.form_fields + form.Fields(
-                form.Field(field.field, **fieldkwargs),**kwargs)
 
 
-    def isSelected(self):
+class MultiFormBase(form.FormBase):
 
-        # XXX implement this, default is selected
-        return True
-    
-    def actions():
-        def _getActions(self):
-            return self.baseRow_actions
-        return property(_getActions)
-    
-    actions = actions()
 
-    def availableActions(self, actions=None):
-        if actions is not None:
-            return availableActions(self, actions)
-        else:
-            return availableActions(self, self.actions)
+    itemFormFactory = ItemFormBase
+    subForms={}
+    form_fields = []
 
-    def setUpWidgets(self, ignore_request=False):
-        self.adapters = {}
-        self.widgets = form.setUpEditWidgets(
-            self.form_fields, self.prefix, self.context, self.request,
-            adapters=self.adapters, ignore_request=ignore_request
-            )
 
+    def update(self):
+        super(MultiFormBase,self).update()
+        for form in self.subForms.values():
+            form.update()
 
-def isFormEditMode(form, action):
-    return form.mode == 'edit' and action.label in form.table.config.actions
+    def setUpWidgets(self, *args, **kw):
+        super(MultiFormBase,self).setUpWidgets(*args,**kw)
+        self.subForms = {}
+        self.setUpForms(*args, **kw)
 
-
-def isFormDisplayMode(form, action):
-    return form.mode == 'display' and action.label in form.table.config.actions
-
-
-
-
-class MultiForm(BrowserView):
-
-    implements(IMultiForm)
-
-    label = u''
-
-    prefix = ''
-
-    status = ''
-
-    errors = ()
-
-    forms = {}
-
-    mode = 'display'
-
-    newmode = None
-
-    actions =[]
-
-    selectionField = ISelectable(['isSelected'])
-
-    def availableActions(self, actions=None):
-        if actions is not None:
-            return availableActions(self, actions)
-        else:
-            return availableActions(self, self.actions)
-
-    def setPrefix(self, prefix):
-        self.prefix = prefix
-
-    def checkEditMode(self, prefix):
-        if "%s.actions.apply" % prefix in self.request.form:
-            self.mode = 'edit'
-        
-    def setNewMode(self):
-        if self.newmode is not None:
-            self.mode = self.newmode
-            self.newmode = None
-            self.form_reset = True
-
-    def itemForm(self, item, **kwargs):
-        return ItemFormBase(item, self, **kwargs)
-
-    def setUpForms(self, ignore_request=False):
-        self.forms = {}
-        # the context must implement IReadMapping
+    def setUpForms(self, *args, **kw):
         for name,item in self.context.items():
-            itemPrefix = self.prefix and self.prefix+'.' or '' + name
-            if not ignore_request:
-                # check edit mode with row actions
-                self.checkEditMode(itemPrefix)
-            kwargs = {
-                'omit_readonly':False,
-                'render_context':True,
-                'fields':self.form_fields}
-            self.forms[name] = self.itemForm(item, **kwargs)
-            self.forms[name].setPrefix(itemPrefix)
+            prefix = (self.prefix and self.prefix+'.' or '') + name
+            subForm = self.itemFormFactory(item,self.request,self)
+            subForm.setPrefix(prefix)
+            subForm.setUpWidgets(*args, **kw)
+            self.subForms[name] = subForm
 
-                                            
-    def resetForm(self):
-        self.setUpForms(ignore_request=True)
-        for fo in self.forms.values():
-            fo.resetForm()
 
-    def validate(self, action, data):
-        # XXX implement this
-#        return (getFormsData(self.widgets, self.prefix, data)
-#                + checkInvariants(self.form_fields, data))
-        return ()
-
-#    def availableActions(self):
-#        return availableActions(self, self.actions)
-
-    form_result = None
-    form_reset = True
-
-    def update(self):
-        # check edit mode with table actions
-        self.checkEditMode(self.prefix)
-        
-        self.setUpForms()
-        self.form_reset = False
-
-        data = {}
-        errors, action = form.handleSubmit(self.actions, data, self.validate)
-        self.errors = errors
-
-        if errors:
-            self.status = _('There were errors')
-            result = action.failure(data, errors)
-        elif errors is not None:
-            result = action.success(data)
-        else:
-            result = None
-#        result = None     
-
-        self.form_result = result
-
-        if not errors:
-            for fo in self.forms.values():
-                fo.update()
-                if fo.errors:
-                    self.errors = list(self.errors) + list(fo.errors)
-                self.form_reset = self.form_reset or fo.form_reset
-                if fo.newmode is not None:
-                    self.newmode = fo.newmode
-
-            if self.errors:
-                self.status = _('There were errors')
-                self.form_reset = False
-                self.newmode = None
-
-        # if actions toggle the mode
-        self.setNewMode()
-
-        if self.form_reset:
-            # build up new forms
-            self.resetForm()
-            form_reset = False
-
-    def error_views(self):
-        for error in self.errors:
-            if isinstance(error, basestring):
-                yield error
-            else:
-                view = zapi.getMultiAdapter(
-                    (error, self.request),
-                    IWidgetInputErrorView)
-                title = getattr(error, 'widget_title', None) # XXX duck typing
-                if title:
-                    yield '%s: %s' % (title, view.snippet())
-                else:
-                    yield view.snippet()
-                    
-    def handOverAction(self, name, label):
-        """hand over action table action to row actions with same name, label
-           of all selected rows."""
-        selected = False
-        for formName,fo in self.forms.values():
-            if self.isSelected(formName):
-                # hand over submit of action to all forms
-                action = "%s.actions.%s" % (fo.prefix, name)
-                self.request.form[action] = label
-                selected = True
-        return selected
-        
-    def __call__(self):
-        self.update()
-        return self.render()
-
-
-    def isSelected(self,name):
-        # XXX get from request
-        return False
-
-    def render(self):
-        # if the form has been updated, it will already have a result
-        if self.form_result is None:
-            if self.form_reset:
-                # we reset, in case data has changed in a way that
-                # causes the widgets to have different data
-                self.resetForm()
-                self.form_reset = False
-            self.form_result = self.template()
-        return self.form_result



More information about the Checkins mailing list