[Checkins] SVN: z3c.form/branches/adamg-objectwidget/src/z3c/form/ major refactor between form.object and form.browser.object

Adam Groszer agroszer at gmail.com
Thu Oct 9 05:53:51 EDT 2008


Log message for revision 91918:
  major refactor between form.object and form.browser.object
  also seem to got the where to pass object as value
  

Changed:
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py

-=-
Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py	2008-10-09 08:42:21 UTC (rev 91917)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py	2008-10-09 09:53:49 UTC (rev 91918)
@@ -1,3 +1,21 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ObjectWidget browser related classes
+
+$Id$
+"""
+
 __docformat__ = "reStructuredText"
 import zope.component
 import zope.interface
@@ -3,60 +21,13 @@
 import zope.schema.interfaces
 
-from z3c.form import form, interfaces
-from z3c.form.widget import Widget, FieldWidget
+from z3c.form import interfaces, object
+from z3c.form.widget import FieldWidget
 from z3c.form.browser import widget
-from z3c.form.error import MultipleErrors
 
-from z3c.form.object import ObjectSubForm
+class ObjectWidget(widget.HTMLFormElement, object.ObjectWidget):
+    zope.interface.implements(interfaces.IObjectWidget)
 
-class ObjectWidget(widget.HTMLFormElement, Widget):
-    zope.interface.implementsOnly(interfaces.IObjectWidget)
-
     klass = u'object-widget'
-    subform = None
-    _value = interfaces.NOVALUE
 
-    def updateWidgets(self):
-        if self._value is not interfaces.NOVALUE:
-            self.subform = ObjectSubForm(self._value, self)
-            ignore = None
-        else:
-            self.subform = ObjectSubForm(None, self)
-            ignore = True
-
-        self.subform.update(ignore)
-
-    def update(self):
-        super(ObjectWidget, self).update()
-        self.updateWidgets()
-
-    @apply
-    def value():
-        """This invokes updateWidgets on any value change e.g. update/extract."""
-        def get(self):
-            return self.extract()
-        def set(self, value):
-            if isinstance(value, tuple):
-                try:
-                    value = interfaces.IDataConverter(self).toFieldValue(value)
-                    self._value = value
-                except (zope.schema.ValidationError,
-                    ValueError, MultipleErrors), error:
-                    pass
-            else:
-                self._value = value
-
-            # ensure that we apply our new values to the widgets
-            self.updateWidgets()
-        return property(get, set)
-
-    def extract(self, default=interfaces.NOVALUE):
-        if self.name+'-empty-marker' in self.request:
-            self.updateWidgets()
-            rv=self.subform.extractData()
-            return rv
-        else:
-            return default
-
 @zope.component.adapter(zope.schema.interfaces.IObject, interfaces.IFormLayer)
 @zope.interface.implementer(interfaces.IFieldWidget)

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt	2008-10-09 08:42:21 UTC (rev 91917)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt	2008-10-09 09:53:49 UTC (rev 91918)
@@ -140,6 +140,7 @@
   >>> v = MySubObject()
   >>> v.foofield = 42
   >>> v.barfield = 666
+  >>> v.__marker__ = "ThisMustStayTheSame"
 
 
   >>> widget.ignoreContext = False
@@ -179,10 +180,16 @@
     </body>
   </html>
 
+The widget's value is NOVALUE until it gets a request:
+
+  >>> widget.value
+  <NOVALUE>
+
 Let's fill in some values via the request:
 
   >>> widget.request = TestRequest(form={'subobject.widgets.foofield':u'2',
-  ...                                    'subobject.widgets.barfield':u'999'})
+  ...                                    'subobject.widgets.barfield':u'999',
+  ...                                    'subobject-empty-marker':u'1'})
   >>> widget.update()
   >>> print widget.render()
   <html>
@@ -216,13 +223,27 @@
     </body>
   </html>
 
+  >>> v = widget.value
+  >>> v
+  <z3c.form.testing.MySubObject object at ...>
+  >>> v.foofield
+  2
+  >>> v.barfield
+  999
 
+The marker must stay (we have to modify the same object):
 
+  >>> v.__marker__
+  'ThisMustStayTheSame'
+
+
+
 Error handling is next. Let's use the value "bad" (an invalid integer literal)
 as input for our internal (sub) widget.
 
   >>> widget.request = TestRequest(form={'subobject.widgets.foofield':u'2',
-  ...                                    'subobject.widgets.barfield':u'bad'})
+  ...                                    'subobject.widgets.barfield':u'bad',
+  ...                                    'subobject-empty-marker':u'1'})
 
 
   >>> widget.update()
@@ -261,11 +282,13 @@
     </body>
   </html>
 
+  >>> widget.value
+  Traceback (most recent call last):
+  ...
+  MultipleErrors
 
 
 
-
-
 In forms
 ========
 
@@ -300,9 +323,6 @@
   >>> request = TestRequest()
   >>> myaddform =  MyAddForm(root, request)
 
-#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
-
-
   >>> myaddform.update()
 
 As usual, the form contains a widget manager with the expected widget
@@ -492,9 +512,6 @@
   99
 
   >>> editform.request = request
-
-#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
-
   >>> editform.update()
 
 Until we have updated the form:
@@ -806,3 +823,139 @@
       </form>
     </body>
   </html>
+
+
+
+
+
+
+
+
+
+
+#Object in an Object situation
+#=============================
+#
+#
+#We define an interface containing a subobject, and an addform for it:
+#
+#  >>> from z3c.form import form, field
+#  >>> from z3c.form.testing import MyComplexObject, IMyComplexObject
+#
+#Note, that creating an object will print some information about it:
+#
+#  >>> class MyAddForm(form.AddForm):
+#  ...     fields = field.Fields(IMyComplexObject)
+#  ...     def create(self, data):
+#  ...         print "MyAddForm.create", str(data)
+#  ...         return MyObject(**data)
+#  ...     def add(self, obj):
+#  ...         self.context[obj.name] = obj
+#  ...     def nextURL(self):
+#  ...         pass
+#
+#We create the form and try to update it:
+#
+#  >>> request = TestRequest()
+#
+##>>> from pub.dbgpclient import brk; brk('192.168.32.1')
+#
+#  >>> myaddform =  MyAddForm(root, request)
+#
+##>>> from pub.dbgpclient import brk; brk('192.168.32.1')
+#
+#
+#  >>> myaddform.update()
+#
+#As usual, the form contains a widget manager with the expected widget
+#
+#  >>> myaddform.widgets.keys()
+#  ['subobject', 'name']
+#  >>> myaddform.widgets.values()
+#  [<ObjectWidget 'form.widgets.subobject'>, <TextWidget 'form.widgets.name'>]
+#
+#The addform has our ObjectWidget which in turn contains a subform:
+#
+#  >>> myaddform.widgets['subobject'].subform
+#  <z3c.form.object.ObjectSubForm object at ...>
+#
+#Which in turn contains the sub-widgets:
+#
+#  >>> myaddform.widgets['subobject'].subform.widgets.keys()
+#  ['subfield', 'moofield']
+#
+#  >>> myaddform.widgets['subobject'].subform.widgets['subfield'].subform.widgets.keys()
+#  ['foofield', 'barfield']
+#
+#If we want to render the addform, we must give it a template:
+#
+#  >>> addTemplate(myaddform)
+#
+#Now rendering the addform renders the subform as well:
+#
+#  >>> print myaddform.render()
+#  <html xmlns="http://www.w3.org/1999/xhtml">
+#    <body>
+#      <form action=".">
+#        <div class="row">
+#          <label for="form-widgets-subobject">my object</label>
+#          <div class="object-widget required">
+#            <div class="label">
+#              <label for="form-widgets-subobject-widgets-subfield">
+#                <span>Second-subobject</span>
+#                <span class="required">*</span>
+#              </label>
+#            </div>
+#            <div class="widget">
+#              <div class="object-widget required">
+#                <div class="label">
+#                  <label for="form-widgets-subobject-widgets-subfield-widgets-foofield">
+#                    <span>My foo field</span>
+#                    <span class="required">*</span>
+#                  </label>
+#                </div>
+#                <div class="widget">
+#                  <input class="text-widget required int-field"
+#                  id="form-widgets-subobject-widgets-subfield-widgets-foofield"
+#                  name="form.widgets.subobject.widgets.subfield.widgets.foofield"
+#                  type="text" value="1,111">
+#                </div>
+#                <div class="label">
+#                  <label for="form-widgets-subobject-widgets-subfield-widgets-barfield">
+#                    <span>My dear bar</span>
+#                  </label>
+#                </div>
+#                <div class="widget">
+#                  <input class="text-widget int-field"
+#                  id="form-widgets-subobject-widgets-subfield-widgets-barfield"
+#                  name="form.widgets.subobject.widgets.subfield.widgets.barfield"
+#                  type="text" value="2,222">
+#                </div>
+#                <input name="form.widgets.subobject.widgets.subfield-empty-marker" type="hidden" value="1">
+#              </div>
+#            </div>
+#            <div class="label">
+#              <label for="form-widgets-subobject-widgets-moofield">
+#                <span>Something</span>
+#                <span class="required">*</span>
+#              </label>
+#            </div>
+#            <div class="widget">
+#              <input class="text-widget required textline-field"
+#              id="form-widgets-subobject-widgets-moofield"
+#              name="form.widgets.subobject.widgets.moofield" type="text" value="">
+#            </div>
+#            <input name="form.widgets.subobject-empty-marker" type="hidden" value="1">
+#          </div>
+#        </div>
+#        <div class="row">
+#          <label for="form-widgets-name">name</label>
+#          <input class="text-widget required textline-field"
+#          id="form-widgets-name" name="form.widgets.name" type="text" value="">
+#        </div>
+#        <div class="action">
+#          <input class="submit-widget button-field" id="form-buttons-add" name="form.buttons.add" type="submit" value="Add">
+#        </div>
+#      </form>
+#    </body>
+#  </html>

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py	2008-10-09 08:42:21 UTC (rev 91917)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py	2008-10-09 09:53:49 UTC (rev 91918)
@@ -21,7 +21,7 @@
 import zope.location
 import zope.schema.interfaces
 
-from z3c.form import interfaces, util
+from z3c.form import interfaces, util, error
 from z3c.form.error import MultipleErrors
 from z3c.form.widget import AfterWidgetUpdateEvent
 
@@ -272,9 +272,9 @@
         for name, widget in self.items():
             if widget.mode == interfaces.DISPLAY_MODE:
                 continue
-            raw = widget.extract()
             value = widget.field.missing_value
             try:
+                raw = widget.extract()
                 if raw is not interfaces.NOVALUE:
                     value = interfaces.IDataConverter(widget).toFieldValue(raw)
                 zope.component.getMultiAdapter(

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py	2008-10-09 08:42:21 UTC (rev 91917)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py	2008-10-09 09:53:49 UTC (rev 91918)
@@ -23,7 +23,7 @@
 
 from z3c.form.converter import BaseDataConverter
 
-from z3c.form import form, interfaces, util
+from z3c.form import form, interfaces, util, widget
 from z3c.form.field import Fields
 from z3c.form.error import MultipleErrors
 from z3c.form.i18n import MessageFactory as _
@@ -84,69 +84,122 @@
 
         self._validate()
 
-
 class ObjectConverter(BaseDataConverter):
     """Data converter for IObjectWidget."""
 
     zope.component.adapts(
         zope.schema.interfaces.IObject, interfaces.IObjectWidget)
 
-    factory = None
-
-    def _fields(self):
-        return zope.schema.getFields(self.field.schema)
-
     def toWidgetValue(self, value):
         """Just dispatch it."""
         if value is self.field.missing_value:
-            return None
+            return interfaces.NOVALUE
 
         return value
 
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        if value is interfaces.NOVALUE:
+            return self.field.missing_value
+
+        return value
+
+
+class ObjectWidget(widget.Widget):
+    zope.interface.implements(interfaces.IObjectWidget)
+
+    subform = None
+    _value = interfaces.NOVALUE
+    _updating = False
+
+    def updateWidgets(self):
+        if self._value is not interfaces.NOVALUE:
+            self.subform = ObjectSubForm(self._value, self)
+            ignore = None
+        else:
+            self.subform = ObjectSubForm(None, self)
+            ignore = True
+
+        self.subform.update(ignore)
+
+    def update(self):
+        #very-very-nasty: skip raising exceptions in extract while we're updating
+        self._updating = True
+        try:
+            super(ObjectWidget, self).update()
+            self.updateWidgets()
+        finally:
+            self._updating = False
+
+    @apply
+    def value():
+        """This invokes updateWidgets on any value change e.g. update/extract."""
+        def get(self):
+            return self.extract()
+        def set(self, value):
+            if isinstance(value, tuple):
+                try:
+                    value = interfaces.IDataConverter(self).toFieldValue(value)
+                    self._value = value
+                except (zope.schema.ValidationError,
+                    ValueError, MultipleErrors), error:
+                    pass
+            else:
+                self._value = value
+
+            # ensure that we apply our new values to the widgets
+            self.updateWidgets()
+        return property(get, set)
+
     def createObject(self, value):
         #keep value passed, maybe some subclasses want it
         #nasty: value here is the raw extracted from the widget's subform
         #in the form of (value-dict, (error1, error2))
 
-        if self.factory is None:
-            name = self.field.schema.__module__+'.'+self.field.schema.__name__
-            creator = zope.component.queryMultiAdapter(
-                (self.widget.context, self.widget.request,
-                 self.widget.form, self.widget),
-                interfaces.IObjectFactory,
-                name=name)
-            if creator:
-                obj = creator(value)
-            else:
-                raise ValueError("No IObjectFactory adapter registered for %s" %
-                                 name)
+        name = self.field.schema.__module__+'.'+self.field.schema.__name__
+        creator = zope.component.queryMultiAdapter(
+            (self.context, self.request,
+             self.form, self),
+            interfaces.IObjectFactory,
+            name=name)
+        if creator:
+            obj = creator(value)
         else:
-            #this is creepy, do we need this?
-            #there seems to be no way to dispatch???
-            obj = self.factory()
+            raise ValueError("No IObjectFactory adapter registered for %s" %
+                             name)
 
         return obj
 
-    def toFieldValue(self, value):
-        """See interfaces.IDataConverter"""
-        if value is interfaces.NOVALUE:
-            return self.field.missing_value
+    def extract(self, default=interfaces.NOVALUE):
+        if self.name+'-empty-marker' in self.request:
+            self.updateWidgets()
 
-        if value[1]:
-            raise MultipleErrors(value[1])
+            value = self.subform.extractData()
+            #value here is (data-dict, (error1, error2))
 
-        if (self.widget._value is not interfaces.NOVALUE
-            and not self.widget.subform.ignoreContext):
-            obj = self.widget._value
-        else:
-            obj = self.createObject(value)
+            if value[1]:
+                #very-very-nasty: skip raising exceptions in extract while we're updating
+                if self._updating:
+                    return default
+                raise MultipleErrors(value[1])
 
-        obj = self.field.schema(obj)
+            if (self._value is not interfaces.NOVALUE
+                and not self.subform.ignoreContext):
+                obj = self._value
+            else:
+                obj = self.createObject(value)
 
-        for name, f in self._fields().items():
-            setattr(obj, name, value[0][name])
-        return obj
+            obj = self.field.schema(obj)
 
+            for name in zope.schema.getFieldNames(self.field.schema):
+                try:
+                    setattr(obj, name, value[0][name])
+                except KeyError:
+                    pass
+            return obj
+        else:
+            return default
+
 class FactoryAdapter(object):
     """Most basic-default factory adapter"""
 



More information about the Checkins mailing list