[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/Forms - IWidget.py:1.5 Utility.py:1.5 Widget.py:1.5 Converter.py:NONE IConverter.py:NONE

Jim Fulton jim@zope.com
Mon, 11 Nov 2002 15:52:58 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/Forms
In directory cvs.zope.org:/tmp/cvs-serv19223

Modified Files:
	IWidget.py Utility.py Widget.py 
Removed Files:
	Converter.py IConverter.py 
Log Message:
Changed the getName and getTitle methods to name and title properties.
The name property is just the prefix, a dot and the field name. The
field name is used to generate request variables, although, widgets
using multiple input elements will often need to generate more
specific names.

Added haveData method to test whether there is user input.

Added required widget attribute that simply delegates to the
underlying field required attribute.

Added support for CustomWidgets in automatic widget setup code.

Added fieldNames utility function to get the names of fields defined
by a schema in definition order.

Added haveWidgets data to easily tests whether any widgets have
data. This can be used to determine if a form was submitted or is
being viewed for the first time. It's better to use an explicit
request variable, set by a submit button to test this.

Moved the setPrefix, name, and setData machinery to the generic widget
code.


=== Zope3/lib/python/Zope/App/Forms/IWidget.py 1.4 => 1.5 ===
--- Zope3/lib/python/Zope/App/Forms/IWidget.py:1.4	Mon Oct 28 18:52:31 2002
+++ Zope3/lib/python/Zope/App/Forms/IWidget.py	Mon Nov 11 15:52:57 2002
@@ -36,24 +36,34 @@
     def getData():
         """Return converted and validated widget data.
 
+        If there is no user input and the field is required, then a
+        MissingInputError will be raised.
+
+        If there is no user input and the field is not required, then
+        the field default value will be returned.
+
         A WidgetInputError is returned in the case of one or more
         errors encountered, inputting, converting, or validating the data.
         """
 
-    def getName():
-        """Return the unique name for the widget.
+    def haveData():
+        """Is there input data for the field
 
-        This must be unique within a set of widgets.
+        Return True if there is data and False otherwise.
         """
 
-    def getTitle():
-        """Return the widget title.
+    name = Attribute("""The uniquewidget name
 
-        This is the text that will be used to label the widget.
-        """
+        This must be unique within a set of widgets.
+        """)
+
+    title = Attribute("The widget title")
+
+    required = Attribute("Flag indicating whether the field is required")
+    
     def setData(value):
         """Set the default data for the widget.
 
-        The given value should only be used if the user hasn't entered
-        any data and the data should be a valid data for the field.
+        The given value should be used even if the user has entered
+        data.
         """


=== Zope3/lib/python/Zope/App/Forms/Utility.py 1.4 => 1.5 ===
--- Zope3/lib/python/Zope/App/Forms/Utility.py:1.4	Wed Oct 30 04:19:20 2002
+++ Zope3/lib/python/Zope/App/Forms/Utility.py	Mon Nov 11 15:52:57 2002
@@ -37,11 +37,11 @@
 from Zope.ComponentArchitecture import getView, getDefaultViewName
 from Zope.Schema.IField import IField
 from Zope.App.Forms.IWidget import IWidget
-from Zope.App.Forms.Exceptions import WidgetsError
-from Zope.Proxy.ContextWrapper import ContextWrapper
+from Zope.App.Forms.Exceptions import WidgetsError, MissingInputError
+from Zope.ComponentArchitecture.IView import IViewFactory
 
 
-def setUpWidget(view, name, field, value=None, prefix=None):
+def setUpWidget(view, name, field, value=None, prefix=None, force=0):
     """Set up a single view widget
 
     The widget will be an attribute of the view. If there is already
@@ -54,14 +54,25 @@
 
     # Has a (custom) widget already been defined?
     widget = getattr(view, name, None)
+
     if widget is None:
         # There isn't already a widget, create one
-        field = ContextWrapper(field, view.context, name=name)
+        field = field.bind(view.context)
         vname = getDefaultViewName(field, view.request)
         widget = getView(field, vname, view.request)
         setattr(view, name, widget)
+
     else:
         # We have an attribute of the right name, it it really a widget
+        if IViewFactory.isImplementedBy(widget):
+            # This is a view factory, probably a custom widget.
+            # Try to make it into a widget.
+            field = field.bind(view.context)
+            widget = widget(field, view.request)
+            if IWidget.isImplementedBy(widget):
+                # Yee ha! We have a widget now, save it
+                setattr(view, name, widget)            
+            
         if not IWidget.isImplementedBy(widget):
             raise TypeError(
                 "The %s view attribute named, %s, should be a widget, "
@@ -71,10 +82,22 @@
     if prefix:
         widget.setPrefix(prefix)
 
-    if value is not None:
+    if value is not None and (force or not widget.haveData()):
         widget.setData(value)
 
-def setUpWidgets(view, schema, prefix=None, **kw):
+def fieldNames(schema):
+
+    names = []
+    for name in schema:
+        field = schema[name]
+        if IField.isImplementedBy(field):
+            names.append((field.order, name))
+
+    names.sort()
+
+    return [name[1] for name in names]
+
+def setUpWidgets(view, schema, prefix=None, force=0, **kw):
     """Set up widgets for the fields defined by a schema
 
     Initial data is provided by keyword arguments.
@@ -84,9 +107,10 @@
         field = schema[name]
         if IField.isImplementedBy(field):
             # OK, we really got a field
-            setUpWidget(view, name, field, kw.get(name), prefix=prefix)
+            setUpWidget(view, name, field, kw.get(name),
+                        prefix=prefix, force=force)
 
-def setUpEditWidgets(view, schema, content=None, prefix=None):
+def setUpEditWidgets(view, schema, content=None, prefix=None, force=0):
     """Set up widgets for the fields defined by a schema
 
     Initial data is provided by content object attributes.
@@ -101,15 +125,37 @@
         if IField.isImplementedBy(field):
             # OK, we really got a field
             setUpWidget(view, name, field, getattr(content, name, None),
-                        prefix = prefix)
+                        prefix = prefix, force = force)
+
+def haveWidgetsData(view, schema):
+    """Collect the user-entered data defined by a schema
+
+    Data is collected from view widgets. For every field in the
+    schema, we look for a view of the same name and get it's data.
+
+    The data are returned in a mapping from field name to value.
+    """
 
-def getWidgetsData(view, schema):
+    for name in schema:
+        field = schema[name]
+        if IField.isImplementedBy(field):
+            # OK, we really got a field
+            if  getattr(view, name).haveData():
+                return True
+
+    return False
+
+def getWidgetsData(view, schema, required=1):
     """Collect the user-entered data defined by a schema
 
     Data is collected from view widgets. For every field in the
     schema, we look for a view of the same name and get it's data.
 
     The data are returned in a mapping from field name to value.
+
+    If the required argument is true, then all of the data defined by
+    the schema will be returned. If some data are missing from the
+    input, an error will be raised.
     """
 
     result = {}
@@ -119,32 +165,48 @@
         field = schema[name]
         if IField.isImplementedBy(field):
             # OK, we really got a field
-            try:
-                result[name] = getattr(view, name).getData()
-            except Exception, v:
-                errors.append(v)
+            widget = getattr(view, name)
+            if widget.haveData():
+                try:
+                    result[name] = widget.getData()
+                except Exception, v:
+                    errors.append(v)
+            elif required:
+                raise MissingInputError(
+                    widget.name, widget.title, name)
 
     if errors:
         raise WidgetsError(*errors)
     
     return result
 
-def getWidgetsDataForContent(view, schema, content=None):
+def getWidgetsDataForContent(view, schema, content=None, required=0):
     """Collect the user-entered data defined by a schema
 
     Data is collected from view widgets. For every field in the
     schema, we look for a view of the same name and get it's data.
 
     The data are assigned to the given content object.
+
+    If the required argument is true, then all of the data defined by
+    the schema will be set. If some data are missing from the
+    input, an error will be raised.
     """
-    data = getWidgetsData(view, schema)
+    
+    data = getWidgetsData(view, schema, required)
     
     if content is None:
         content = view.context
 
-    for name in schema:
-        field = schema[name]
-        if IField.isImplementedBy(field):
-            # OK, we really got a field
-            setattr(content, name, getattr(view, name).getData())
+    errors = []
+
+    for name in data:
+        # OK, we really got a field
+        try:
+            setattr(content, name, data[name])
+        except Exception, v:
+            errors.append(v)
+                
+    if errors:
+        raise WidgetsError(*errors)
 


=== Zope3/lib/python/Zope/App/Forms/Widget.py 1.4 => 1.5 ===
--- Zope3/lib/python/Zope/App/Forms/Widget.py:1.4	Mon Oct 28 18:52:31 2002
+++ Zope3/lib/python/Zope/App/Forms/Widget.py	Mon Nov 11 15:52:57 2002
@@ -17,6 +17,7 @@
 from IWidget import IWidget
 from Zope.Schema.Exceptions import ValidationError
 from Zope.App.Forms.Exceptions import WidgetInputError
+from Zope.ComponentArchitecture.IView import IViewFactory
 
 class Widget(object):
     """Mix-in class providing some functionality common accross view types
@@ -25,9 +26,13 @@
 
     __implements__ = IWidget
 
+    _prefix = 'field.'
+    _data = None
+
     def __init__(self, context, request):
         self.context = context
         self.request = request
+        self.name = self._prefix + context.__name__
 
     # See Zope.App.Forms.IWidget.IWidget
     propertyNames = []
@@ -37,25 +42,28 @@
         if name in self.propertyNames:
             return getattr(self, name, None)
 
-    def getName(self):
-        return self.context.getName()
+    def setPrefix(self, prefix):
+        if not prefix.endswith("."):
+            prefix += '.'
+        self._prefix = prefix
+        self.name = prefix + self.context.__name__
+
+    def setData(self, value):
+        self._data = value
 
-    def getTitle(self):
-        return self.context.title
+    def haveData(self):
+        raise TypeError("haveData has not been implemented")
 
     def getData(self):
-        raw = self._getRawData()
-        value = self._convert(raw)
-        try:
-            self.context.validate(value)
-        except ValidationError, v:
-            raise WidgetInputError(self.getName(), self.getTitle(), v)
+        raise TypeError("haveData has not been implemented")
 
-        return value
+    title = property(lambda self: self.context.title)
+
+    required = property(lambda self: self.context.required)
 
 class CustomWidget(object):
     """Custom Widget."""
-    __instance_implements__ = IWidget
+    __implements__ = IViewFactory
 
     def __init__(self, widget, **kw):
         self.widget = widget
@@ -66,14 +74,3 @@
         for item in self.kw.items():
             setattr(instance, item[0], item[1])
         return instance
-                  
-class Customizer:
-    """Objects for making shorthands for creating CustomWidgets
-    """
-    
-    def __init__(self, widget_class):
-        self.__widget_class = widget_class
-
-    def __call__(self, **kw):
-        # XXX should have error checking here!
-        return CustomWidgets(self.__widget_class, **kw)

=== Removed File Zope3/lib/python/Zope/App/Forms/Converter.py ===

=== Removed File Zope3/lib/python/Zope/App/Forms/IConverter.py ===