[Checkins] SVN: z3c.form/branches/adamg-objectwidget/src/z3c/form/ reworked to pass dict to the widget
Adam Groszer
agroszer at gmail.com
Mon Oct 13 09:26:41 EDT 2008
Log message for revision 92116:
reworked to pass dict to the widget
still problems with sub-sub widgets
Changed:
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.txt
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt 2008-10-13 13:18:24 UTC (rev 92115)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/browser/object.txt 2008-10-13 13:26:41 UTC (rev 92116)
@@ -16,15 +16,17 @@
>>> from z3c.form import testing
>>> testing.setupFormDefaults()
+ >>> import zope.component
+ >>> from z3c.form import datamanager
+ >>> zope.component.provideAdapter(datamanager.DictionaryField)
+
>>> from z3c.form.testing import IMySubObject
>>> from z3c.form.testing import IMySecond
>>> from z3c.form.testing import MySubObject
>>> from z3c.form.testing import MySecond
- >>> import zope.component
-
>>> from z3c.form.object import registerFactoryAdapter
>>> registerFactoryAdapter(IMySubObject, MySubObject)
>>> registerFactoryAdapter(IMySecond, MySecond)
@@ -223,13 +225,20 @@
</body>
</html>
- >>> v = widget.value
+Widget value comes from the request:
+
+ >>> wv = widget.value
+ >>> wv
+ {'foofield': 2, 'barfield': 999}
+
+But our object will not be modified, since there was no "apply"-like control.
+
>>> v
<z3c.form.testing.MySubObject object at ...>
>>> v.foofield
- 2
+ 42
>>> v.barfield
- 999
+ 666
The marker must stay (we have to modify the same object):
@@ -241,7 +250,7 @@
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',
+ >>> widget.request = TestRequest(form={'subobject.widgets.foofield':u'55',
... 'subobject.widgets.barfield':u'bad',
... 'subobject-empty-marker':u'1'})
@@ -261,7 +270,7 @@
<input class="text-widget required int-field"
id="subobject-widgets-foofield"
name="subobject.widgets.foofield"
- type="text" value="2">
+ type="text" value="55">
</div>
<div class="label">
<label for="subobject-widgets-barfield">
@@ -282,13 +291,26 @@
</body>
</html>
+Getting the widget value raises the widget errors:
+
>>> widget.value
Traceback (most recent call last):
...
MultipleErrors
+Our object still must retain the old values:
+ >>> v
+ <z3c.form.testing.MySubObject object at ...>
+ >>> v.foofield
+ 42
+ >>> v.barfield
+ 666
+ >>> v.__marker__
+ 'ThisMustStayTheSame'
+
+
In forms
========
@@ -301,6 +323,28 @@
>>> import z3c.form.object
>>> zope.component.provideAdapter(z3c.form.object.ObjectConverter)
+Forms and our objectwidget fire events on add and edit, setup a subscriber
+for those:
+
+ >>> eventlog = []
+ >>> import zope.lifecycleevent
+ >>> @zope.component.adapter(zope.lifecycleevent.ObjectModifiedEvent)
+ ... def logEvent(event):
+ ... eventlog.append(event)
+ >>> zope.component.provideHandler(logEvent)
+ >>> @zope.component.adapter(zope.lifecycleevent.ObjectCreatedEvent)
+ ... def logEvent2(event):
+ ... eventlog.append(event)
+ >>> zope.component.provideHandler(logEvent2)
+
+ >>> def printEvents():
+ ... for event in eventlog:
+ ... print str(event)
+ ... if isinstance(event, zope.lifecycleevent.ObjectModifiedEvent):
+ ... for attr in event.descriptions:
+ ... print attr.interface
+ ... print attr.attributes
+
We define an interface containing a subobject, and an addform for it:
>>> from z3c.form import form, field
@@ -323,6 +367,7 @@
>>> request = TestRequest()
>>> myaddform = MyAddForm(root, request)
+
>>> myaddform.update()
As usual, the form contains a widget manager with the expected widget
@@ -418,6 +463,7 @@
... 'form.widgets.subobject-empty-marker':u'1',
... 'form.buttons.add':'Add'})
>>> myaddform.request = request
+
>>> myaddform.update()
MyAddForm.create {'subobject': <z3c.form.testing.MySubObject object at ...>,
'name': u'first'}
@@ -437,6 +483,21 @@
>>> root['first'].subobject.barfield
99
+Let's see our event log:
+
+ >>> len(eventlog)
+ 4
+
+ >>> printEvents()
+ <zope.app.event.objectevent.ObjectCreatedEvent object at ...>
+ <zope.app.event.objectevent.ObjectModifiedEvent object at ...>
+ <InterfaceClass z3c.form.testing.IMySubObject>
+ ('foofield', 'barfield')
+ <zope.app.event.objectevent.ObjectCreatedEvent object at ...>
+ <zope.app.container.contained.ContainerModifiedEvent object at ...>
+
+ >>> eventlog=[]
+
Let's try to edit that newly added object:
>>> class MyEditForm(form.EditForm):
@@ -521,6 +582,22 @@
>>> root['first'].subobject.barfield
55
+Let's see our event log:
+
+ >>> len(eventlog)
+ 2
+
+ >>> printEvents()
+ <zope.app.event.objectevent.ObjectModifiedEvent object at ...>
+ <InterfaceClass z3c.form.testing.IMySubObject>
+ ('foofield', 'barfield')
+ <zope.app.event.objectevent.ObjectModifiedEvent object at ...>
+ <InterfaceClass z3c.form.testing.IMyObject>
+ ('subobject',)
+
+ >>> eventlog=[]
+
+
After the update the form says that the values got updated and renders the new
values:
@@ -617,8 +694,14 @@
... 'form.buttons.apply':'Apply'})
>>> editform.request = request
+ >>> eventlog=[]
>>> editform.update()
+Eventlog must be clean:
+
+ >>> len(eventlog)
+ 0
+
Watch for the error message in the HTML:
it has to appear at the field itself and at the top of the form:
@@ -777,6 +860,28 @@
999
+#Let's make an other error, by typing a newline into name:
+#
+# >>> request = TestRequest(form={
+# ... 'form.widgets.subobject.widgets.foofield':u'1',
+# ... 'form.widgets.subobject.widgets.barfield':u'2',
+# ... 'form.widgets.name':u'first\nline',
+# ... 'form.widgets.subobject-empty-marker':u'1',
+# ... 'form.buttons.apply':'Apply'})
+#
+# >>> editform.request = request
+# >>> eventlog=[]
+# >>> editform.update()
+
+THIS IS BAD:
+
+ #>>> len(eventlog)
+ #0
+ #>>> printEvents()
+ #<zope.app.event.objectevent.ObjectModifiedEvent object at 0x97f688c>
+ #<InterfaceClass z3c.form.testing.IMySubObject>
+ #('foofield', 'barfield')
+
Simple but often used use-case is the display form:
>>> editform = MyEditForm(root['first'], TestRequest())
@@ -862,7 +967,7 @@
#
# >>> myaddform = MyAddForm(root, request)
#
-##>>> from pub.dbgpclient import brk; brk('192.168.32.1')
+# >>> from pub.dbgpclient import brk; brk('192.168.32.1')
#
#
# >>> myaddform.update()
Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py 2008-10-13 13:18:24 UTC (rev 92115)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/field.py 2008-10-13 13:26:41 UTC (rev 92116)
@@ -250,7 +250,9 @@
# Step 5: Set the form
widget.form = self.form
# Optimization: set both interfaces here, rather in step 4 and 5: alsoProvides is quite slow
- zope.interface.alsoProvides(widget, interfaces.IContextAware, interfaces.IFormAware)
+ zope.interface.alsoProvides(widget,
+ interfaces.IContextAware,
+ interfaces.IFormAware)
# Step 6: Set some variables
widget.ignoreContext = self.ignoreContext
widget.ignoreRequest = self.ignoreRequest
Modified: z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py
===================================================================
--- z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py 2008-10-13 13:18:24 UTC (rev 92115)
+++ z3c.form/branches/adamg-objectwidget/src/z3c/form/object.py 2008-10-13 13:26:41 UTC (rev 92116)
@@ -20,6 +20,8 @@
import zope.interface
import zope.component
import zope.schema
+import zope.event
+import zope.lifecycleevent
from z3c.form.converter import BaseDataConverter
@@ -84,6 +86,9 @@
self._validate()
+ def getContent(self):
+ return self.__parent__._value
+
class ObjectConverter(BaseDataConverter):
"""Data converter for IObjectWidget."""
@@ -95,13 +100,63 @@
if value is self.field.missing_value:
return interfaces.NOVALUE
- return value
+ retval = {}
+ for name in zope.schema.getFieldNames(self.field.schema):
+ retval[name] = getattr(value, name, interfaces.NOVALUE)
+ return retval
+
+ def createObject(self, value):
+ #keep value passed, maybe some subclasses want it
+ #value here is the raw extracted from the widget's subform
+ #in the form of a dict key:fieldname, value:fieldvalue
+
+ 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)
+
+ return obj
+
def toFieldValue(self, value):
"""See interfaces.IDataConverter"""
if value is interfaces.NOVALUE:
return self.field.missing_value
+ if self.widget.subform.ignoreContext:
+ obj = self.createObject(value)
+ else:
+ obj = getattr(self.widget.context, self.field.__name__)
+
+ obj = self.field.schema(obj)
+
+ names = []
+ for name in zope.schema.getFieldNames(self.field.schema):
+ try:
+ oldval = getattr(obj, name, interfaces.NOVALUE)
+ if (oldval != value[name]
+ or zope.schema.interfaces.IObject.providedBy(
+ self.field.schema[name])
+ ):
+ setattr(obj, name, value[name])
+ names.append(name)
+ except KeyError:
+ pass
+
+ if names:
+ zope.event.notify(
+ zope.lifecycleevent.ObjectModifiedEvent(obj,
+ zope.lifecycleevent.Attributes(self.field.schema, *names)))
+ return obj
+
+
return value
@@ -137,39 +192,12 @@
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
-
+ 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))
- 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:
- raise ValueError("No IObjectFactory adapter registered for %s" %
- name)
-
- return obj
-
def extract(self, default=interfaces.NOVALUE):
if self.name+'-empty-marker' in self.request:
self.updateWidgets()
@@ -178,25 +206,23 @@
#value here is (data-dict, (error1, error2))
if value[1]:
- #very-very-nasty: skip raising exceptions in extract while we're updating
+ #very-very-nasty: skip raising exceptions in extract
+ #while we're updating
if self._updating:
+ #very-very-nasty: kill all errors on subwidgets
+ if self.subform.widgets.errors:
+ pass
+ #from pub.dbgpclient import brk; brk('192.168.32.1')
+
+ self.subform.widgets.errors = ()
+ for w in self.subform.widgets.values():
+ w.error=None
+
return default
raise MultipleErrors(value[1])
- if (self._value is not interfaces.NOVALUE
- and not self.subform.ignoreContext):
- obj = self._value
- else:
- obj = self.createObject(value)
+ return value[0]
- 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
@@ -217,7 +243,9 @@
def __call__(self, value):
#value is the extracted data from the form
- return self.factory()
+ obj = self.factory()
+ zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+ return obj
def __repr__(self):
return '<%s %r>' % (self.__class__.__name__, self.__name__)
More information about the Checkins
mailing list