[Checkins] SVN: grok/trunk/src/grok/ Renamed the form method applyChanges to applyData as proposed on the

Philipp von Weitershausen philikon at philikon.de
Tue Apr 17 13:27:26 EDT 2007


Log message for revision 74203:
  Renamed the form method applyChanges to applyData as proposed on the
  mailinglist: http://mail.zope.org/pipermail/grok-dev/2007-April/000638.html
  
  * applyData no longer calls zope.formlib.form.applyChanges, it uses a helper
    in grok.formlib that *optionally* supports "update mode" (setting form data
    on an object only when the data needs to be updated). Update mode is disabled
    on normal forms, but it is enabled on EditForms.
  
    This was the main motivation for this refactoring (see mailinglist thread above).
  
  * applyData continues to send IObjectModifiedEvents. It does that now by employing
    another helper in grok.formlib. The event that's sent carries all the necessary
    information that's required by the event spec (which field of which schema was
    changed).
  
  * applyData no longer returns a simple boolean to indicate whether changes had 
    to be  made to an object. It now returns a dictionary that contains the information
    *which* fields of which interfaces had to be changed (interfaces are the keys
    of the dict, a list of attributes are the values).
  
    If no changes are made to the object, an empty dict is returned. Given Python's
    semantics for booleans, this is still compatible with the old return value of
    applyChanges. For example, you can still do::
  
      if self.applyData(...):
          print 'Changes were made'
  
  Backwards compatibility for applyChanges was provided for 1 month.
  
  

Changed:
  U   grok/trunk/src/grok/components.py
  U   grok/trunk/src/grok/formlib.py
  U   grok/trunk/src/grok/ftests/form/actions.py
  U   grok/trunk/src/grok/ftests/form/addform.py
  A   grok/trunk/src/grok/ftests/form/addform_applydata.py
  A   grok/trunk/src/grok/ftests/form/addform_catalog.py
  A   grok/trunk/src/grok/ftests/form/editform_applydata.py
  A   grok/trunk/src/grok/ftests/form/editform_applydata_classfields.py
  A   grok/trunk/src/grok/ftests/form/editform_applydata_schema.py
  U   grok/trunk/src/grok/interfaces.py

-=-
Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/components.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -17,12 +17,16 @@
 import os
 import persistent
 import urllib
+import datetime
+import warnings
+import pytz
 import simplejson
 
 from zope import component
 from zope import interface
 from zope import schema
 from zope import event
+from zope.interface.common import idatetime
 from zope.lifecycleevent import ObjectModifiedEvent
 from zope.publisher.browser import BrowserPage
 from zope.publisher.interfaces import NotFound
@@ -46,7 +50,7 @@
 from zope.app.container.interfaces import IReadContainer
 from zope.app.component.site import SiteManagerContainer
 
-from grok import util, interfaces
+from grok import util, interfaces, formlib
 
 
 # These base grokkers exist in grok.components because they are meant
@@ -415,12 +419,6 @@
         self.update_form()
         return self.render()
 
-    def applyChanges(self, obj, **data):
-        if form.applyChanges(obj, self.form_fields, data, self.adapters):
-            event.notify(ObjectModifiedEvent(obj))
-            return True
-        return False
-
 class Form(GrokForm, form.FormBase, View):
     # We're only reusing the form implementation from zope.formlib, we
     # explicitly don't want to inherit the interface semantics (mostly
@@ -429,6 +427,17 @@
 
     template = default_form_template
 
+    def applyData(self, obj, **data):
+        return formlib.apply_data_event(obj, self.form_fields, data,
+                                        self.adapters)
+
+    # BBB -- to be removed in June 2007
+    def applyChanges(self, obj, **data):
+        warnings.warn("The 'applyChanges' method on forms is deprecated "
+                      "and will disappear by June 2007. Please use "
+                      "'applyData' instead.", DeprecationWarning, 2)
+        return bool(self.applyData(obj, **data))
+
 class AddForm(Form):
     pass
 
@@ -440,6 +449,34 @@
 
     template = default_form_template
 
+    def applyData(self, obj, **data):
+        return formlib.apply_data_event(obj, self.form_fields, data,
+                                        self.adapters, update=True)
+
+    # BBB -- to be removed in June 2007
+    def applyChanges(self, obj, **data):
+        warnings.warn("The 'applyChanges' method on forms is deprecated "
+                      "and will disappear by June 2007. Please use "
+                      "'applyData' instead.", DeprecationWarning, 2)
+        return bool(self.applyData(obj, **data))
+
+    @formlib.action("Apply")
+    def handle_edit_action(self, **data):
+        if self.applyData(self.context, **data):
+            formatter = self.request.locale.dates.getFormatter(
+                'dateTime', 'medium')
+
+            try:
+                time_zone = idatetime.ITZInfo(self.request)
+            except TypeError:
+                time_zone = pytz.UTC
+
+            self.status = "Updated on %s" % formatter.format(
+                datetime.datetime.now(time_zone)
+                )
+        else:
+            self.status = 'No changes'
+
 class DisplayForm(GrokForm, form.DisplayFormBase, View):
     # We're only reusing the form implementation from zope.formlib, we
     # explicitly don't want to inherit the interface semantics (mostly

Modified: grok/trunk/src/grok/formlib.py
===================================================================
--- grok/trunk/src/grok/formlib.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/formlib.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -1,5 +1,5 @@
 import types
-from zope import interface
+from zope import interface, event, lifecycleevent
 from zope.interface.interfaces import IInterface
 from zope.formlib import form
 from zope.schema.interfaces import IField
@@ -88,3 +88,60 @@
         if seen_iface.extends(iface):
             return True
     return False
+
+def apply_data(context, form_fields, data, adapters=None, update=False):
+    """Save form data (``data`` dict) on a ``context`` object.
+
+    This is a beefed up version of zope.formlib.form.applyChanges().
+    It allows you to specify whether values should be compared with
+    the attributes on already existing objects or not, using the
+    ``update`` parameter.
+
+    Unlike zope.formlib.form.applyChanges(), it will return a
+    dictionary of interfaces and their fields that were changed.  This
+    is necessary to appropriately send IObjectModifiedEvents.
+    """
+    if adapters is None:
+        adapters = {}
+
+    changes = {}
+
+    for form_field in form_fields:
+        field = form_field.field
+        # Adapt context, if necessary
+        interface = field.interface
+        adapter = adapters.get(interface)
+        if adapter is None:
+            if interface is None:
+                adapter = context
+            else:
+                adapter = interface(context)
+            adapters[interface] = adapter
+
+        name = form_field.__name__
+        newvalue = data.get(name, form_field) # using form_field as marker
+
+        if update:
+            if ((newvalue is not form_field) and
+                (field.get(adapter) != newvalue)):
+                field.set(adapter, newvalue)
+                changes.setdefault(interface, []).append(name)
+        else:
+            if newvalue is not form_field:
+                field.set(adapter, newvalue)
+                changes.setdefault(interface, []).append(name)
+
+    return changes
+
+def apply_data_event(context, form_fields, data, adapters=None, update=False):
+    """Like apply_data, but also sends an IObjectModifiedEvent.
+    """
+    changes = apply_data(context, form_fields, data, adapters, update)
+
+    if changes:
+        descriptions = []
+        for interface, names in changes.items():
+            descriptions.append(lifecycleevent.Attributes(interface, *names))
+        event.notify(lifecycleevent.ObjectModifiedEvent(context, *descriptions))
+
+    return changes

Modified: grok/trunk/src/grok/ftests/form/actions.py
===================================================================
--- grok/trunk/src/grok/ftests/form/actions.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/actions.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -2,7 +2,7 @@
 Using the @grok.action decorator, different actions can be defined on
 a grok.Form. When @grok.action is used, the default behaviour (the
 'Apply' action) is not available anymore, but it can triggered
-manually by calling self.applyChanges(object, data).
+manually by calling self.applyData(object, data).
 
   >>> import grok
   >>> from grok.ftests.form.actions import Mammoth
@@ -57,14 +57,14 @@
 class Edit(grok.EditForm):
     @grok.action("Apply")
     def handle_apply(self, **data):
-        if self.applyChanges(self.context, **data):
+        if self.applyData(self.context, **data):
             self.status = 'Modified!'
         else:
             self.status = 'No changes!'
 
     @grok.action("Hairy")
     def handle_hairy(self, **data):
-        self.applyChanges(self.context, **data)
+        self.applyData(self.context, **data)
         self.context.size += " and hairy"
 
 class Meet(grok.Form):

Modified: grok/trunk/src/grok/ftests/form/addform.py
===================================================================
--- grok/trunk/src/grok/ftests/form/addform.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/addform.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -1,5 +1,5 @@
 """
-We can use grok.Form to render an add form for objects:
+We can use grok.AddForm to render an add form for objects:
 
   >>> import grok
   >>> from grok.ftests.form.addform import Zoo, Mammoth
@@ -17,7 +17,10 @@
   >>> print browser.contents
   Hi, my name is Manfred the Mammoth, and I\'m "Really big"
 
-  >>> browser.open("http://localhost/zoo/@@addmammothapplychanges")
+Instead of calling an object constructor with the form data, we can
+also use the ``applyData`` method to store the data on the object.
+
+  >>> browser.open("http://localhost/zoo/@@addmammothapplydata")
   >>> browser.getControl(name="form.name").value = "Ellie the Mammoth"
   >>> browser.getControl(name="form.size").value = "Really small"
   >>> browser.getControl("Add entry").click()
@@ -57,11 +60,11 @@
         self.context['manfred'] = manfred = Mammoth(**data)
         self.redirect(self.url(manfred))
 
-class AddMammothApplyChanges(AddMammoth):
+class AddMammothApplyData(AddMammoth):
 
     @grok.action('Add entry')
     def add(self, **data):
-        # instantiate Mammoth and then use self.applyChanges()
+        # instantiate Mammoth and then use self.applyData()
         self.context['ellie'] = ellie = Mammoth()
-        self.applyChanges(ellie, **data)
+        self.applyData(ellie, **data)
         self.redirect(self.url(ellie))

Added: grok/trunk/src/grok/ftests/form/addform_applydata.py
===================================================================
--- grok/trunk/src/grok/ftests/form/addform_applydata.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/addform_applydata.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -0,0 +1,69 @@
+"""
+We can use AddFrom.applyData to save changes to a newly created
+object.  The object doesn't yet need to have the attributes that are
+going to be set on it.
+
+  >>> import grok
+  >>> from grok.ftests.form.addform_applydata import Zoo, Mammoth
+  >>> grok.grok('grok.ftests.form.addform_applydata')
+  >>> getRootFolder()["zoo"] = Zoo()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+
+AddForm.applyData() sends an IObjectModifiedEvent after having
+modified the object.  Its return value is True in a Boolean sense when
+the object has been modified:
+
+  >>> browser.open("http://localhost/zoo/@@addmammoth")
+  >>> browser.getControl(name="form.name").value = "Ellie the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really small"
+  >>> browser.getControl("Add entry").click()
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  IMammoth: name, size
+  >>> print browser.contents
+  There were changes according to applyData.
+
+  >>> browser.open("http://localhost/zoo/ellie")
+  >>> print browser.contents
+  Hi, my name is Ellie the Mammoth, and I\'m "Really small"
+
+"""
+import grok
+from zope import schema, interface
+
+class Zoo(grok.Container):
+    pass
+
+class IMammoth(interface.Interface):
+    name = schema.TextLine(title=u"Name")
+    size = schema.TextLine(title=u"Size")
+
+class Mammoth(grok.Model):
+    grok.implements(IMammoth)
+
+class Index(grok.View):
+    grok.context(Mammoth)
+    def render(self):
+        return 'Hi, my name is %s, and I\'m "%s"' % (self.context.name,
+                                                     self.context.size)
+
+class AddMammoth(grok.AddForm):
+    grok.context(Zoo)
+
+    form_fields = grok.AutoFields(IMammoth)
+
+    @grok.action('Add entry')
+    def add(self, **data):
+        self.context['ellie'] = ellie = Mammoth()
+        if self.applyData(ellie, **data):
+            return 'There were changes according to applyData.'
+        return 'There were no changes according to applyData.'
+
+ at grok.subscribe(Mammoth, grok.IObjectModifiedEvent)
+def notify_change_event(mammoth, event):
+    print ("An IObjectModifiedEvent was sent for a mammoth with the "
+           "following changes:")
+    for descr in event.descriptions:
+        print descr.interface.__name__ + ": " + ", ".join(descr.attributes)


Property changes on: grok/trunk/src/grok/ftests/form/addform_applydata.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/src/grok/ftests/form/addform_catalog.py
===================================================================
--- grok/trunk/src/grok/ftests/form/addform_catalog.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/addform_catalog.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -0,0 +1,89 @@
+"""
+Thanks to Zope's event system, newly added objects are automatically
+catalogued, should a catalog be present.
+
+  >>> import grok
+  >>> from grok.ftests.form.addform_catalog import Zoo, Mammoth
+  >>> grok.grok('grok.ftests.form.addform_catalog')
+  >>> getRootFolder()["zoo"] = Zoo()
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+
+Let's demonstrate that an object that has not been added to a
+container yet can still be modified using a form's applyData method.
+Event though this method triggers an IObjectModifiedEvent, the catalog
+won't be bothered by this.  It will start the indexation when the
+object has been *added* to a container, not before.
+
+  >>> browser.open("http://localhost/zoo/@@addmammoth")
+  >>> browser.getControl(name="form.name").value = "Ellie the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really small"
+  >>> browser.getControl("Add entry").click()
+
+  >>> browser.open("http://localhost/zoo/ellie")
+  >>> print browser.contents
+  Hi, my name is Ellie the Mammoth, and I\'m "Really small"
+
+Let's ensure the catalog has actually indexed the object with the
+right value:
+
+  >>> browser.open("http://localhost/zoo/search")
+  >>> print browser.contents
+  We found Ellie!
+
+"""
+import grok
+from zope import schema, interface, component
+from zope.app.intid import IntIds
+from zope.app.intid.interfaces import IIntIds
+from zope.app.catalog.catalog import Catalog
+from zope.app.catalog.interfaces import ICatalog
+from zope.app.catalog.field import FieldIndex
+
+def setup_catalog(catalog):
+    catalog['name'] = FieldIndex('name', IMammoth)
+
+class Zoo(grok.Site, grok.Container):
+    grok.local_utility(IntIds, provides=IIntIds)
+    grok.local_utility(Catalog, provides=ICatalog, setup=setup_catalog)
+
+class IMammoth(interface.Interface):
+    name = schema.TextLine(title=u"Name")
+    size = schema.TextLine(title=u"Size")
+
+class Mammoth(grok.Model):
+    grok.implements(IMammoth)
+
+class Index(grok.View):
+    grok.context(Mammoth)
+
+    def render(self):
+        return 'Hi, my name is %s, and I\'m "%s"' % (self.context.name,
+                                                     self.context.size)
+
+class Search(grok.View):
+    grok.context(Zoo)
+
+    def render(self):
+        catalog = component.getUtility(ICatalog)
+        query = ('Ellie the Mammoth', 'Ellie the Mammoth')
+        results = catalog.searchResults(name=query)
+        if len(list(results)) == 1:
+            return 'We found Ellie!'
+        return "Couldn't find Ellie."
+
+class AddMammoth(grok.AddForm):
+    grok.context(Zoo)
+
+    form_fields = grok.AutoFields(IMammoth)
+
+    @grok.action('Add entry')
+    def add(self, **data):
+        # First apply the form data, thus triggering an
+        # IObjectModifiedEvent.  This test case demonstrates that this
+        # isn't a problem.
+        ellie = Mammoth()
+        self.applyData(ellie, **data)
+        self.context['ellie'] = ellie


Property changes on: grok/trunk/src/grok/ftests/form/addform_catalog.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/src/grok/ftests/form/editform_applydata.py
===================================================================
--- grok/trunk/src/grok/ftests/form/editform_applydata.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/editform_applydata.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -0,0 +1,92 @@
+"""
+A grok.EditForm uses applyData in update mode to save the form data on
+the object.  Update mode means that only those fields are changed on
+the object that need to be changed.
+
+  >>> import grok
+  >>> from grok.ftests.form.editform_applydata import Mammoth
+  >>> grok.grok('grok.ftests.form.editform_applydata')
+  >>> getRootFolder()["manfred"] = Mammoth('Manfred the Mammoth', 'Really big')
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+
+If we don't change any of the fields, there will no object modified
+event and applyData will report no changes:
+
+  >>> browser.open("http://localhost/manfred/@@edit")
+  >>> browser.getControl("Apply").click()
+  >>> 'No changes' in browser.contents
+  True
+
+If we change one field, only that attribute will be changed.  The
+object modified event also reflects that:
+
+  >>> browser.getControl(name="form.name").value = "Manfred the Big Mammoth"
+  >>> browser.getControl("Apply").click()
+  The 'name' property is being set.
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  name
+  >>> 'Updated' in browser.contents
+  True
+
+Let's change the other field:
+
+  >>> browser.getControl(name="form.size").value = "Enormously big"
+  >>> browser.getControl("Apply").click()
+  The 'size' property is being set.
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  size
+  >>> 'Updated' in browser.contents
+  True
+
+And finally let's change both fields:
+
+  >>> browser.getControl(name="form.name").value = "Manfred the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really big"
+  >>> browser.getControl("Apply").click()
+  The 'name' property is being set.
+  The 'size' property is being set.
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  name, size
+  >>> 'Updated' in browser.contents
+  True
+
+"""
+import grok
+from zope import schema
+
+class Mammoth(grok.Model):
+
+    def __init__(self, name='', size=''):
+        self._name = name
+        self._size = size
+
+    def getName(self):
+        return self._name
+    def setName(self, value):
+        print "The 'name' property is being set."
+        self._name = value
+    name = property(getName, setName)
+
+    def getSize(self):
+        return self._size
+    def setSize(self, value):
+        print "The 'size' property is being set."
+        self._size = value
+    size = property(getSize, setSize)
+
+class Edit(grok.EditForm):
+
+    form_fields = grok.Fields(
+        name = schema.TextLine(title=u"Name"),
+        size = schema.TextLine(title=u"Size")
+        )
+
+ at grok.subscribe(Mammoth, grok.IObjectModifiedEvent)
+def notify_change_event(mammoth, event):
+    print ("An IObjectModifiedEvent was sent for a mammoth with the "
+           "following changes:")
+    for descr in event.descriptions:
+        print ", ".join(descr.attributes)


Property changes on: grok/trunk/src/grok/ftests/form/editform_applydata.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/src/grok/ftests/form/editform_applydata_classfields.py
===================================================================
--- grok/trunk/src/grok/ftests/form/editform_applydata_classfields.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/editform_applydata_classfields.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -0,0 +1,75 @@
+"""
+A grok.EditForm uses applyData in update mode to save the form data on
+the object.  Update mode means that only those fields are changed on
+the object that need to be changed.
+
+This is essentially the same narrative as 'editform_applydata'. Here
+we test the whole procedure on fields defined on the model class:
+
+  >>> import grok
+  >>> from grok.ftests.form.editform_applydata_classfields import Mammoth
+  >>> grok.grok('grok.ftests.form.editform_applydata_classfields')
+  >>> getRootFolder()["manfred"] = mammoth = Mammoth()
+  >>> mammoth.name = 'Manfred the Mammoth'
+  >>> mammoth.size = 'Really big'
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+
+If we don't change any of the fields, there will no object modified
+event and applyData will report no changes:
+
+  >>> browser.open("http://localhost/manfred/@@edit")
+  >>> browser.getControl("Apply").click()
+  >>> 'No changes' in browser.contents
+  True
+
+If we change one field, only that attribute will be changed.  The
+object modified event also reflects that:
+
+  >>> browser.getControl(name="form.name").value = "Manfred the Big Mammoth"
+  >>> browser.getControl("Apply").click()
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  name
+  >>> 'Updated' in browser.contents
+  True
+
+Let's change the other field:
+
+  >>> browser.getControl(name="form.size").value = "Enormously big"
+  >>> browser.getControl("Apply").click()
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  size
+  >>> 'Updated' in browser.contents
+  True
+
+And finally let's change both fields:
+
+  >>> browser.getControl(name="form.name").value = "Manfred the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really big"
+  >>> browser.getControl("Apply").click()
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  name, size
+  >>> 'Updated' in browser.contents
+  True
+
+"""
+import grok
+from zope import schema
+
+
+class Mammoth(grok.Model):
+    class fields:
+        name = schema.TextLine(title=u"Name")
+        size = schema.TextLine(title=u"Size")
+
+class Edit(grok.EditForm):
+    pass
+
+ at grok.subscribe(Mammoth, grok.IObjectModifiedEvent)
+def notify_change_event(mammoth, event):
+    print ("An IObjectModifiedEvent was sent for a mammoth with the "
+           "following changes:")
+    for descr in event.descriptions:
+        print ", ".join(descr.attributes)


Property changes on: grok/trunk/src/grok/ftests/form/editform_applydata_classfields.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: grok/trunk/src/grok/ftests/form/editform_applydata_schema.py
===================================================================
--- grok/trunk/src/grok/ftests/form/editform_applydata_schema.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/ftests/form/editform_applydata_schema.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -0,0 +1,96 @@
+"""
+A grok.EditForm uses applyData in update mode to save the form data on
+the object.  Update mode means that only those fields are changed on
+the object that need to be changed.
+
+This is essentially the same narrative as 'editform_applydata'. Here
+we test the whole procedure on fields from schemas:
+
+  >>> import grok
+  >>> from grok.ftests.form.editform_applydata_schema import Mammoth
+  >>> grok.grok('grok.ftests.form.editform_applydata_schema')
+  >>> getRootFolder()["manfred"] = Mammoth('Manfred the Mammoth', 'Really big')
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+
+If we don't change any of the fields, there will no object modified
+event and applyData will report no changes:
+
+  >>> browser.open("http://localhost/manfred/@@edit")
+  >>> browser.getControl("Apply").click()
+  >>> 'No changes' in browser.contents
+  True
+
+If we change one field, only that attribute will be changed.  The
+object modified event also reflects that:
+
+  >>> browser.getControl(name="form.name").value = "Manfred the Big Mammoth"
+  >>> browser.getControl("Apply").click()
+  The 'name' property is being set.
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  IMammoth: name
+  >>> 'Updated' in browser.contents
+  True
+
+Let's change the other field:
+
+  >>> browser.getControl(name="form.size").value = "Enormously big"
+  >>> browser.getControl("Apply").click()
+  The 'size' property is being set.
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  IMammoth: size
+  >>> 'Updated' in browser.contents
+  True
+
+And finally let's change both fields:
+
+  >>> browser.getControl(name="form.name").value = "Manfred the Mammoth"
+  >>> browser.getControl(name="form.size").value = "Really big"
+  >>> browser.getControl("Apply").click()
+  The 'name' property is being set.
+  The 'size' property is being set.
+  An IObjectModifiedEvent was sent for a mammoth with the following changes:
+  IMammoth: name, size
+  >>> 'Updated' in browser.contents
+  True
+
+"""
+import grok
+from zope import schema, interface
+
+class IMammoth(interface.Interface):
+    name = schema.TextLine(title=u"Name")
+    size = schema.TextLine(title=u"Size")
+
+class Mammoth(grok.Model):
+    grok.implements(IMammoth)
+
+    def __init__(self, name='', size=''):
+        self._name = name
+        self._size = size
+
+    def getName(self):
+        return self._name
+    def setName(self, value):
+        print "The 'name' property is being set."
+        self._name = value
+    name = property(getName, setName)
+
+    def getSize(self):
+        return self._size
+    def setSize(self, value):
+        print "The 'size' property is being set."
+        self._size = value
+    size = property(getSize, setSize)
+
+class Edit(grok.EditForm):
+    form_fields = grok.AutoFields(IMammoth)
+
+ at grok.subscribe(Mammoth, grok.IObjectModifiedEvent)
+def notify_change_event(mammoth, event):
+    print ("An IObjectModifiedEvent was sent for a mammoth with the "
+           "following changes:")
+    for descr in event.descriptions:
+        print descr.interface.__name__ + ": " + ", ".join(descr.attributes)


Property changes on: grok/trunk/src/grok/ftests/form/editform_applydata_schema.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py	2007-04-17 17:06:25 UTC (rev 74202)
+++ grok/trunk/src/grok/interfaces.py	2007-04-17 17:27:25 UTC (rev 74203)
@@ -353,9 +353,14 @@
         The errors are returned as an iterable.
         """
 
-    def applyChanges(obj, **data):
-        """Apply form data to an object.  Return True if the object
-        had to be modified, False otherwise.
+    def applyData(obj, **data):
+        """Save form data to an object.
+
+        This returns a dictionary with interfaces as keys and lists of
+        field names as values to indicate which fields in which
+        schemas had to be changed in order to save the data.  In case
+        the method works in update mode (e.g. on EditForms) and
+        doesn't have to update an object, the dictionary is empty.
         """
 
 



More information about the Checkins mailing list