[Checkins] SVN: z3c.form/branches/adamg-objectwidget/ first try at the objectwidget,

Adam Groszer agroszer at gmail.com
Tue Oct 7 11:41:25 EDT 2008


Log message for revision 91860:
  first try at the objectwidget,
  missing points:
  - object factory totally bad
  - error display bad
  
  

Changed:
  U   z3c.form/branches/adamg-objectwidget/buildout.cfg
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/configure.zcml
  A   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py
  A   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt
  A   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.zcml
  A   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_display.pt
  A   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_input.pt
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/tests.py
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/widget.py
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/configure.zcml
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/converter.py
  A   z3c.form/branches/adamg-objectwidget/src/z3c/form/dummy.py
  U   z3c.form/branches/adamg-objectwidget/src/z3c/form/interfaces.py

-=-
Modified: z3c.form/branches/adamg-objectwidget/buildout.cfg
===================================================================
--- z3c.form/branches/adamg-objectwidget/buildout.cfg	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/buildout.cfg	2008-10-07 15:41:24 UTC (rev 91860)
@@ -7,6 +7,8 @@
 [versions]
 lxml = 2.1.1
 zope.app.locales = 3.4.5
+z3c.pt = 1.0a9
+z3c.pt.compat = 0.2
 
 [test-environment]
 Z3C_PT_DEBUG = False

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/configure.zcml
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/configure.zcml	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/configure.zcml	2008-10-07 15:41:24 UTC (rev 91860)
@@ -17,4 +17,6 @@
   <include file="textarea.zcml" />
   <include file="textlines.zcml" />
 
+  <include file="object.zcml" />
+
 </configure>

Added: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py	                        (rev 0)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py	2008-10-07 15:41:24 UTC (rev 91860)
@@ -0,0 +1,104 @@
+__docformat__ = "reStructuredText"
+import zope.component
+import zope.interface
+import zope.schema.interfaces
+
+from z3c.form import action, button, form, interfaces
+from z3c.form.i18n import MessageFactory as _
+from z3c.form import interfaces
+from z3c.form.widget import Widget, FieldWidget
+from z3c.form.field import FieldWidgets, Fields
+from z3c.form.browser import widget
+from z3c.form.subform import EditSubForm
+
+class ObjectSubForm(form.BaseForm):
+    zope.interface.implements(interfaces.ISubForm)
+
+    formErrorsMessage = _('There were some errors.')
+    successMessage = _('Data successfully updated.')
+    noChangesMessage = _('No changes were applied.')
+
+    def __init__(self, context, parentWidget):
+        self.context = context
+        self.request = parentWidget.request
+        self.parentWidget = parentWidget
+        self.parentForm = self.__parent__ = parentWidget.form
+
+    def _validate(self):
+        for widget in self.widgets.values():
+            try:
+                # convert widget value to field value
+                converter = interfaces.IDataConverter(widget)
+                value = converter.toFieldValue(widget.value)
+                # validate field value
+                zope.component.getMultiAdapter(
+                    (self.context,
+                     self.request,
+                     self.parentWidget.form,
+                     getattr(widget, 'field', None),
+                     widget),
+                    interfaces.IValidator).validate(value)
+            except (zope.schema.ValidationError, ValueError), error:
+                # on exception, setup the widget error message
+                view = zope.component.getMultiAdapter(
+                    (error, self.request, widget, widget.field,
+                     self.parentWidget.form, self.context),
+                    interfaces.IErrorViewSnippet)
+                view.update()
+                widget.error = view
+
+    def update(self):
+        self.fields = Fields(self.parentWidget.field.schema)
+
+        self.mode = self.parentWidget.mode
+        self.ignoreContext = self.parentWidget.ignoreContext
+        self.ignoreRequest = self.parentWidget.ignoreRequest
+        self.prefix = self.parentWidget.field.__name__
+
+        if interfaces.IFormAware.providedBy(self.parentWidget):
+            self.ignoreReadonly = self.parentWidget.form.ignoreReadonly
+
+        super(ObjectSubForm, self).update()
+
+        self._validate()
+
+
+class ObjectWidget(widget.HTMLFormElement, Widget):
+    zope.interface.implementsOnly(interfaces.IObjectWidget)
+
+    klass = u'object-widget'
+    subform = None
+
+    def _makeSubform(self):
+        #if self.subform is None:
+        try:
+            subobj = getattr(self.context, self.field.__name__)
+        except AttributeError:
+            if self.value:
+                subobj = self.value
+            else:
+                #lame, mostly for adding
+                subobj = self.context
+        self.subform = ObjectSubForm(subobj, self)
+        #self.subform = ObjectSubForm(self.value, self)
+
+    def update(self):
+        super(ObjectWidget, self).update()
+
+        self._makeSubform()
+        self.subform.update()
+
+    def extract(self, default=interfaces.NOVALUE):
+        if self.name+'-empty-marker' in self.request:
+            self._makeSubform()
+            self.subform.update()
+            rv=self.subform.extractData()
+            return rv
+        else:
+            return default
+
+ at zope.component.adapter(zope.schema.interfaces.IObject, interfaces.IFormLayer)
+ at zope.interface.implementer(interfaces.IFieldWidget)
+def ObjectFieldWidget(field, request):
+    """IFieldWidget factory for IObjectWidget."""
+    return FieldWidget(field, ObjectWidget(request))


Property changes on: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.py
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision
Name: svn:eol-style
   + native

Added: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt	                        (rev 0)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt	2008-10-07 15:41:24 UTC (rev 91860)
@@ -0,0 +1,430 @@
+============
+ObjectWidget
+============
+
+The ObjectWidget is about rendering a schema's fields as a single widget.
+
+
+There are way too many preconditions to exercise the ObjectWidget as it
+relies heavily on the from and widget framework.
+It renders the sub-widgets in a sub-form.
+
+In order to not overwhelm you with our set of well-chosen defaults,
+all the default component registrations have been made prior to doing those
+examples:
+
+  >>> from z3c.form import testing
+  >>> testing.setupFormDefaults()
+
+
+
+As for all widgets, the objectwidget must provide the new ``IWidget``
+interface:
+
+  >>> from zope.interface.verify import verifyClass
+  >>> from z3c.form import interfaces
+  >>> from z3c.form.browser import object
+
+  >>> verifyClass(interfaces.IWidget, object.ObjectWidget)
+  True
+
+The widget can be instantiated only using the request:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> request = TestRequest()
+  >>> widget = object.ObjectWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+  >>> widget.id = 'widget-id'
+  >>> widget.name = 'widget.name'
+
+Also, stand-alone widgets need to ignore the context:
+
+  >>> widget.ignoreContext = True
+
+There is no life for ObjectWidget without a schema to render:
+
+  >>> widget.update()
+  Traceback (most recent call last):
+  ...
+  AttributeError: 'NoneType' object has no attribute 'schema'
+
+This schema is specified by the field:
+
+  >>> from z3c.form.widget import FieldWidget
+  >>> from z3c.form.dummy import IMySubObject
+  >>> import zope.schema
+  >>> field = zope.schema.Object(
+  ...     __name__='subobject',
+  ...     title=u'my object widget',
+  ...     schema=IMySubObject)
+
+Apply the field nicely with the helper:
+
+  >>> widget = FieldWidget(field, widget)
+
+We also need to register the templates for the widget and request:
+
+  >>> import zope.component
+  >>> from zope.pagetemplate.interfaces import IPageTemplate
+  >>> from z3c.form.testing import getPath
+  >>> from z3c.form.widget import WidgetTemplateFactory
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('object_input.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.IObjectWidget),
+  ...     IPageTemplate, name=interfaces.INPUT_MODE)
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetTemplateFactory(getPath('object_display.pt'), 'text/html'),
+  ...     (None, None, None, None, interfaces.IObjectWidget),
+  ...     IPageTemplate, name=interfaces.DISPLAY_MODE)
+
+We can now render the widget:
+
+  >>> widget.update()
+  >>> print widget.render()
+  <fieldset class="object-widget required" id="subobject" name="subobject">
+    <legend>my object widget</legend>
+    <input class="text-widget required int-field" id="subobject-widgets-foofield"
+           name="subobject.widgets.foofield" type="text" value="1,111">
+    <br>
+    <input class="text-widget required int-field" id="subobject-widgets-barfield"
+           name="subobject.widgets.barfield" type="text" value="2,222">
+    <br>
+    <input name="subobject-empty-marker" type="hidden" value="1">
+  </fieldset>
+
+As you see all sort of default values are rendered.
+
+Let's provide a more meaningful value:
+
+  >>> from z3c.form.dummy import MySubObject
+  >>> v = MySubObject()
+  >>> v.foofield = 42
+  >>> v.barfield = 666
+
+  >>> widget.value = v
+  >>> widget.ignoreContext = False
+
+#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
+
+  >>> widget.update()
+  >>> print widget.render()
+  <fieldset class="object-widget required" id="subobject" name="subobject">
+    <legend>my object widget</legend>
+    <input class="text-widget required int-field" id="subobject-widgets-foofield"
+           name="subobject.widgets.foofield" type="text" value="42">
+    <br>
+    <input class="text-widget required int-field" id="subobject-widgets-barfield"
+           name="subobject.widgets.barfield" type="text" value="666">
+    <br>
+    <input name="subobject-empty-marker" type="hidden" value="1">
+  </fieldset>
+
+Let's fill in some values:
+
+  >>> widget.request = TestRequest(form={'subobject.widgets.foofield':u'2',
+  ...                                    'subobject.widgets.barfield':u'999'})
+  >>> widget.update()
+  >>> print widget.render()
+  <fieldset class="object-widget required" id="subobject" name="subobject">
+    <legend>my object widget</legend>
+    <input class="text-widget required int-field" id="subobject-widgets-foofield"
+           name="subobject.widgets.foofield" type="text" value="2">
+    <br>
+    <input class="text-widget required int-field" id="subobject-widgets-barfield"
+           name="subobject.widgets.barfield" type="text" value="999">
+    <br>
+    <input name="subobject-empty-marker" type="hidden" value="1">
+  </fieldset>
+
+
+Error handling is next. Let's use teh 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'})
+
+
+#  >>> from pub.dbgpclient import brk; brk('192.168.32.1')
+
+  >>> widget.update()
+  >>> print widget.render()
+  <fieldset id="subobject" name="subobject"
+            class="object-widget required">
+  <legend>my object widget</legend>
+      <input id="subobject-widgets-foofield"
+             name="subobject.widgets.foofield"
+             class="text-widget required int-field" value="2"
+             type="text" />
+      <br />
+      <div class="error">
+    <div class="error">The entered value is not a valid integer literal.</div>
+      </div>
+      <input id="subobject-widgets-barfield"
+             name="subobject.widgets.barfield"
+             class="text-widget required int-field" value="bad"
+             type="text" />
+      <br />
+  <input name="subobject-empty-marker" type="hidden" value="1" />
+  </fieldset>
+
+
+
+
+
+
+
+
+
+Do all that fun in add and edit forms too:
+
+We have to provide an adapter first:
+
+  >>> zope.component.provideAdapter(object.ObjectFieldWidget)
+  >>> from z3c.form import converter
+  >>> zope.component.provideAdapter(converter.ObjectConverter)
+
+We define an interface containing a subobject, and an addform for it:
+
+  >>> from z3c.form import form, field
+  >>> from z3c.form.dummy import MyObject, IMyObject
+
+  >>> class MyAddForm(form.AddForm):
+  ...     fields = field.Fields(IMyObject)
+  ...     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()
+  >>> myaddform =  MyAddForm(root, request)
+
+#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
+
+  >>> myaddform.update()
+
+As usually, 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'>]
+
+But now, the addform contains a subform, that the user didn't need to create
+and which is already updated:
+
+  >>> myaddform.widgets['subobject'].subform
+  <z3c.form.browser.object.ObjectSubForm object at ...>
+  >>> myaddform.widgets['subobject'].subform.widgets.keys()
+  ['foofield', 'barfield']
+
+If we want to render the addform, we must give it a template:
+
+  >>> import os
+  >>> from zope.app.pagetemplate import viewpagetemplatefile
+  >>> from z3c.form import tests
+  >>> def addTemplate(form):
+  ...     form.template = viewpagetemplatefile.BoundPageTemplate(
+  ...         viewpagetemplatefile.ViewPageTemplateFile(
+  ...             'simple_edit.pt', os.path.dirname(tests.__file__)), form)
+  >>> 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>
+          <fieldset class="object-widget required" id="form-widgets-subobject" name="form.widgets.subobject">
+            <legend>my object</legend>
+            <input class="text-widget required int-field" id="subobject-widgets-foofield" name="subobject.widgets.foofield" type="text" value="1,111">
+            <br>
+            <input class="text-widget required int-field" id="subobject-widgets-barfield" name="subobject.widgets.barfield" type="text" value="2,222">
+            <br>
+            <input name="form.widgets.subobject-empty-marker" type="hidden" value="1">
+          </fieldset>
+        </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>
+
+Let's try to add an object:
+
+  >>> request = TestRequest(form={'subobject.widgets.foofield':u'66',
+  ...                             'subobject.widgets.barfield':u'99',
+  ...                             'form.widgets.name':u'first',
+  ...                             'form.widgets.subobject-empty-marker':u'1',
+  ...                             'form.buttons.add':'Add'})
+  >>> myaddform.request = request
+  >>> myaddform.update()
+  MyAddForm.create {'subobject': <z3c.form.dummy.MySubObject object at ...>,
+  'name': u'first'}
+
+Wow, it got added:
+
+  >>> root['first']
+  <z3c.form.dummy.MyObject object at ...>
+
+  >>> root['first'].subobject
+  <z3c.form.dummy.MySubObject object at ...>
+
+  >>> root['first'].subobject.foofield
+  66
+
+  >>> root['first'].subobject.barfield
+  99
+
+  >>> myaddform.render()
+  ''
+
+
+
+  >>> class MyEditForm(form.EditForm):
+  ...     fields = field.Fields(IMyObject)
+
+  >>> editform = MyEditForm(root['first'], TestRequest())
+  >>> addTemplate(editform)
+  >>> editform.update()
+
+  >>> print editform.render()
+  <html xmlns="http://www.w3.org/1999/xhtml">
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-subobject">my object</label>
+          <fieldset class="object-widget required" id="form-widgets-subobject" name="form.widgets.subobject">
+            <legend>my object</legend>
+            <input class="text-widget required int-field" id="subobject-widgets-foofield" name="subobject.widgets.foofield" type="text" value="66">
+            <br>
+            <input class="text-widget required int-field" id="subobject-widgets-barfield" name="subobject.widgets.barfield" type="text" value="99">
+            <br>
+            <input name="form.widgets.subobject-empty-marker" type="hidden" value="1">
+          </fieldset>
+        </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="first">
+        </div>
+        <div class="action">
+          <input class="submit-widget button-field" id="form-buttons-apply" name="form.buttons.apply" type="submit" value="Apply">
+        </div>
+      </form>
+    </body>
+  </html>
+
+
+  >>> request = TestRequest(form={'subobject.widgets.foofield':u'43',
+  ...                             'subobject.widgets.barfield':u'55',
+  ...                             'form.widgets.name':u'first',
+  ...                             'form.widgets.subobject-empty-marker':u'1',
+  ...                             'form.buttons.apply':'Apply'})
+
+  >>> editform.request = request
+  >>> editform.update()
+
+  >>> root['first'].subobject.foofield
+  43
+
+  >>> root['first'].subobject.barfield
+  55
+
+  >>> print editform.render()
+  <html xmlns="http://www.w3.org/1999/xhtml">
+    <body>
+      <i>Data successfully updated.</i>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-subobject">my object</label>
+          <fieldset class="object-widget required" id="form-widgets-subobject" name="form.widgets.subobject">
+            <legend>my object</legend>
+            <input class="text-widget required int-field" id="subobject-widgets-foofield" name="subobject.widgets.foofield" type="text" value="43">
+            <br>
+            <input class="text-widget required int-field" id="subobject-widgets-barfield" name="subobject.widgets.barfield" type="text" value="55">
+            <br>
+            <input name="form.widgets.subobject-empty-marker" type="hidden" value="1">
+          </fieldset>
+        </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="first">
+        </div>
+        <div class="action">
+          <input class="submit-widget button-field" id="form-buttons-apply" name="form.buttons.apply" type="submit" value="Apply">
+        </div>
+      </form>
+    </body>
+  </html>
+
+
+  >>> request = TestRequest(form={'subobject.widgets.foofield':u'99',
+  ...                             'subobject.widgets.barfield':u'bad',
+  ...                             'form.widgets.name':u'first',
+  ...                             'form.widgets.subobject-empty-marker':u'1',
+  ...                             'form.buttons.apply':'Apply'})
+
+  >>> editform.request = request
+
+#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
+
+  >>> editform.update()
+  >>> print editform.render()
+  <html xmlns="http://www.w3.org/1999/xhtml">
+    <body>
+      <i>There were some errors.</i>
+      <ul>
+        <li>
+        my object:
+          <div class="error">The entered value is not a valid integer literal.</div>
+        </li>
+      </ul>
+      <form action=".">
+        <div class="row">
+          <b>
+            <div class="error">The entered value is not a valid integer literal.</div>
+          </b>
+          <label for="form-widgets-subobject">my object</label>
+          <fieldset class="object-widget required" id="form-widgets-subobject" name="form.widgets.subobject">
+            <legend>my object</legend>
+            <input class="text-widget required int-field" id="subobject-widgets-foofield" name="subobject.widgets.foofield" type="text" value="99">
+            <br>
+            <div class="error">
+              <div class="error">The entered value is not a valid integer literal.</div>
+            </div>
+            <input class="text-widget required int-field" id="subobject-widgets-barfield" name="subobject.widgets.barfield" type="text" value="bad">
+            <br>
+            <input name="form.widgets.subobject-empty-marker" type="hidden" value="1">
+          </fieldset>
+        </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="first">
+        </div>
+        <div class="action">
+          <input class="submit-widget button-field" id="form-buttons-apply" name="form.buttons.apply" type="submit" value="Apply">
+        </div>
+      </form>
+    </body>
+  </html>
+
+  >>> root['first'].subobject.foofield
+  43
+
+  >>> root['first'].subobject.barfield
+  55


Property changes on: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision
Name: svn:eol-style
   + native

Added: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.zcml
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.zcml	                        (rev 0)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.zcml	2008-10-07 15:41:24 UTC (rev 91860)
@@ -0,0 +1,38 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:z3c="http://namespaces.zope.org/z3c"
+    i18n_domain="z3c.form">
+
+  <class class=".object.ObjectWidget">
+    <require
+        permission="zope.Public"
+        interface="z3c.form.interfaces.IObjectWidget"
+        />
+  </class>
+
+<adapter
+      factory=".object.ObjectFieldWidget"
+      for="zope.schema.interfaces.IObject
+           z3c.form.interfaces.IFormLayer"
+      />
+
+<z3c:widgetTemplate
+      mode="input"
+      widget="z3c.form.interfaces.IObjectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="object_input.pt"
+      />
+<z3c:widgetTemplate
+      mode="hidden"
+      widget="z3c.form.interfaces.IObjectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="object_input.pt"
+      />
+<z3c:widgetTemplate
+      mode="display"
+      widget="z3c.form.interfaces.IObjectWidget"
+      layer="z3c.form.interfaces.IFormLayer"
+      template="object_display.pt"
+      />
+
+</configure>


Property changes on: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.zcml
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision
Name: svn:eol-style
   + native

Added: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_display.pt
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_display.pt	                        (rev 0)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_display.pt	2008-10-07 15:41:24 UTC (rev 91860)
@@ -0,0 +1,30 @@
+<fieldset id="" name="" class="" title="" lang="" disabled=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       class view/klass;
+                       style view/style;
+                       title view/title;
+                       lang view/lang;
+                       onclick view/onclick;
+                       ondblclick view/ondblclick;
+                       onmousedown view/onmousedown;
+                       onmouseup view/onmouseup;
+                       onmouseover view/onmouseover;
+                       onmousemove view/onmousemove;
+                       onmouseout view/onmouseout;
+                       onkeypress view/onkeypress;
+                       onkeydown view/onkeydown;
+                       onkeyup view/onkeyup;
+                       disabled view/disabled;
+                       tabindex view/tabindex;
+                       onfocus view/onfocus;
+                       onblur view/onblur;
+                       onchange view/onchange;">
+<legend tal:content="view/label">foobar</legend>
+
+<tal:block repeat="widget view/subform/widgets/values">
+    <tal:block content="structure widget/render" />
+    <br/>
+</tal:block>
+
+</fieldset>
\ No newline at end of file


Property changes on: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_display.pt
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision
Name: svn:eol-style
   + native

Added: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_input.pt
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_input.pt	                        (rev 0)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_input.pt	2008-10-07 15:41:24 UTC (rev 91860)
@@ -0,0 +1,37 @@
+<fieldset id="" name="" class="" title="" lang="" disabled=""
+       tal:attributes="id view/id;
+                       name view/name;
+                       class view/klass;
+                       style view/style;
+                       title view/title;
+                       lang view/lang;
+                       onclick view/onclick;
+                       ondblclick view/ondblclick;
+                       onmousedown view/onmousedown;
+                       onmouseup view/onmouseup;
+                       onmouseover view/onmouseover;
+                       onmousemove view/onmousemove;
+                       onmouseout view/onmouseout;
+                       onkeypress view/onkeypress;
+                       onkeydown view/onkeydown;
+                       onkeyup view/onkeyup;
+                       disabled view/disabled;
+                       tabindex view/tabindex;
+                       onfocus view/onfocus;
+                       onblur view/onblur;
+                       onchange view/onchange;">
+<legend tal:content="view/label">foobar</legend>
+
+<tal:block repeat="widget view/subform/widgets/values">
+    <div class="error"
+         tal:condition="widget/error">
+      <span tal:replace="structure widget/error/render">error</span>
+    </div>
+    <tal:block content="structure widget/render" />
+    <br/>
+</tal:block>
+
+<input name="field-empty-marker" type="hidden" value="1"
+       tal:attributes="name string:${view/name}-empty-marker" />
+
+</fieldset>
\ No newline at end of file


Property changes on: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object_input.pt
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision
Name: svn:eol-style
   + native

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/tests.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/tests.py	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/tests.py	2008-10-07 15:41:24 UTC (rev 91860)
@@ -91,6 +91,11 @@
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      checker=checker,
                      ),
+        DocFileSuite('object.txt',
+                     setUp=setUp, tearDown=testing.tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     checker=checker,
+                     ),
         DocFileSuite('multi.txt',
                      setUp=setUp, tearDown=testing.tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/widget.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/widget.py	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/widget.py	2008-10-07 15:41:24 UTC (rev 91860)
@@ -54,7 +54,16 @@
         if not self.klass:
             self.klass = unicode(klass)
         else:
-            self.klass += u' ' + unicode(klass)
+            #make sure items are not repeated
+            parts = self.klass.split()+[unicode(klass)]
+            seen = {}
+            unique = []
+            for item in parts:
+                if item in seen:
+                    continue
+                seen[item]=1
+                unique.append(item)
+            self.klass = u' '.join(unique)
 
     def update(self):
         """See z3c.form.interfaces.IWidget"""

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/configure.zcml
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/configure.zcml	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/configure.zcml	2008-10-07 15:41:24 UTC (rev 91860)
@@ -74,6 +74,9 @@
   <adapter
       factory=".converter.MultiConverter"
       />
+  <adapter
+      factory=".converter.ObjectConverter"
+      />
 
   <!-- ITerms -->
   <adapter

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/converter.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/converter.py	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/converter.py	2008-10-07 15:41:24 UTC (rev 91860)
@@ -339,7 +339,7 @@
         """Just dispatch it."""
         if value is self.field.missing_value:
             return []
-        # We relay on the default registered widget, this is probably a 
+        # We relay on the default registered widget, this is probably a
         # restriction for custom widgets. If so use your own MultiWidget and
         # register your own converter which will get the right widget for the
         # used value_type.
@@ -359,10 +359,70 @@
             return self.field.missing_value
         valueType = self.field.value_type._type
         values = [valueType(v) for v in value]
-        # convert the field values to a tuple or list 
+        # convert the field values to a tuple or list
         return collectionType(values)
 
+from z3c.form.dummy import MySubObject
 
+class ObjectConverter(BaseDataConverter):
+    """Data converter for IObjectWidget."""
+
+    zope.component.adapts(
+        zope.schema.interfaces.IObject, interfaces.IObjectWidget)
+
+    factory = MySubObject
+    #factory = None
+
+    def _fields(self):
+        x = zope.schema.getFields(self.field.schema)
+        return x
+
+    def toWidgetValue(self, value):
+        """Just dispatch it."""
+        if value is self.field.missing_value:
+            return None
+
+        return value
+
+        rv = {}
+        for name, field in self._fields().items():
+            #widget = zope.component.getMultiAdapter((field, self.widget.request),
+            #    interfaces.IFieldWidget)
+            #converter = zope.component.getMultiAdapter((field, widget),
+            #    interfaces.IDataConverter)
+
+            v = getattr(value, name, None)
+            #rv[name] = converter.toWidgetValue(v)
+            rv[name] = v
+
+        return (rv, tuple())
+
+    def toFieldValue(self, value):
+        """See interfaces.IDataConverter"""
+        #if self.factory is None:
+        #    adapter = zope.component.queryMultiAdapter(
+        #        (self.widget.context, self.widget.request, self.widget.form,
+        #         self.field.schema, self.widget),
+        #        interfaces.IValue, name='default')
+        #    if adapter:
+        #        obj = adapter.get()
+        #else:
+        #    obj = self.factory()
+
+        #this is creepy
+        if value[1]:
+            raise value[1][0].error
+        
+        obj = self.factory()
+        for name, f in self._fields().items():
+            try:
+                setattr(obj, name, value[0][name])
+            except KeyError:
+                #smells like an input error?
+                pass
+        return obj
+
+
 class BoolSingleCheckboxDataConverter(BaseDataConverter):
     "A special converter between boolean fields and single checkbox widgets."
 

Added: z3c.form/branches/adamg-objectwidget/src/z3c/form/dummy.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/dummy.py	                        (rev 0)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/dummy.py	2008-10-07 15:41:24 UTC (rev 91860)
@@ -0,0 +1,80 @@
+############
+# REMOVE this after we found a good solution for factory
+
+
+import zope.interface
+import zope.schema
+from zope.schema.fieldproperty import FieldProperty
+
+class IMySubObject(zope.interface.Interface):
+    foofield = zope.schema.Int(default=1111)
+    barfield = zope.schema.Int(default=2222)
+
+class MySubObject(object):
+    zope.interface.implements(IMySubObject)
+
+    foofield = FieldProperty(IMySubObject['foofield'])
+    barfield = FieldProperty(IMySubObject['barfield'])
+
+class IMySecond(zope.interface.Interface):
+    subfield = zope.schema.Object(schema=IMySubObject)
+    moofield = zope.schema.TextLine(title=u"Something")
+
+class MySecond(object):
+    zope.interface.implements(IMySecond)
+
+    subfield = FieldProperty(IMySecond['subfield'])
+    moofield = FieldProperty(IMySecond['moofield'])
+
+
+class IMyObject(zope.interface.Interface):
+    subobject = zope.schema.Object(title=u'my object', schema=IMySubObject)
+    name = zope.schema.TextLine(title=u'name')
+
+class MyObject(object):
+    zope.interface.implements(IMyObject)
+    def __init__(self, name=u'', subobject=None):
+        self.subobject=subobject
+        self.name=name
+
+
+import zope.interface
+import zope.component
+import zope.schema.interfaces
+
+from z3c.form import interfaces
+
+
+class FactoryAdapter(object):
+    """ """
+
+    zope.interface.implements(interfaces.IValue)
+    zope.component.adapts(zope.interface.Interface, interfaces.IFormLayer,
+        interfaces.IForm, zope.schema.interfaces.IField, interfaces.IWidget)
+
+    factory = None
+
+    def __init__(self, context, request, form, field, widget):
+        self.context = context
+        self.request = request
+        self.form = form
+        self.field = field
+        self.widget = widget
+
+    def get(self):
+        return self.factory()
+
+    def __repr__(self):
+        return '<%s %r>' % (self.__class__.__name__, self.__name__)
+
+class MySubObjectFactory(FactoryAdapter):
+    zope.component.adapts(zope.interface.Interface, interfaces.IFormLayer,
+        interfaces.IForm, IMySubObject, interfaces.IWidget)
+
+    factory = MySubObject
+
+class MySecondFactory(FactoryAdapter):
+    zope.component.adapts(zope.interface.Interface, interfaces.IFormLayer,
+        interfaces.IForm, IMySecond, interfaces.IWidget)
+
+    factory = MySecond
\ No newline at end of file


Property changes on: z3c.form/branches/adamg-objectwidget/src/z3c/form/dummy.py
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision
Name: svn:eol-style
   + native

Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/interfaces.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/interfaces.py	2008-10-07 15:40:37 UTC (rev 91859)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/interfaces.py	2008-10-07 15:41:24 UTC (rev 91860)
@@ -508,6 +508,8 @@
 class IPasswordWidget(ITextWidget):
     """Password widget."""
 
+class IObjectWidget(IWidget):
+    """Object widget.""" 
 
 class IWidgets(IManager):
     """A widget manager"""



More information about the Checkins mailing list