[Zope3-checkins] CVS: Zope3/src/zope/app/browser/form - vocabularywidget.py:1.28

Gary Poster gary@zope.com
Tue, 3 Jun 2003 10:16:42 -0400


Update of /cvs-repository/Zope3/src/zope/app/browser/form
In directory cvs.zope.org:/tmp/cvs-serv27277

Modified Files:
	vocabularywidget.py 
Log Message:
change haveData to have correct semantics.  refactor a bit.



=== Zope3/src/zope/app/browser/form/vocabularywidget.py 1.27 => 1.28 ===
--- Zope3/src/zope/app/browser/form/vocabularywidget.py:1.27	Mon Jun  2 17:09:38 2003
+++ Zope3/src/zope/app/browser/form/vocabularywidget.py	Tue Jun  3 10:16:11 2003
@@ -31,6 +31,7 @@
 from zope.schema.interfaces import IIterableVocabulary, IVocabularyQuery
 from zope.schema.interfaces import IIterableVocabularyQuery
 from zope.schema.interfaces import IVocabularyTokenized
+from zope.schema.interfaces import ValidationError
 
 
 # These widget factories delegate to the vocabulary on the field.
@@ -88,7 +89,7 @@
     return view
 
 
-class IterableVocabularyQuery:
+class IterableVocabularyQuery(object):
     """Simple query object used to invoke the simple selection mechanism."""
 
     __implements__ = IIterableVocabularyQuery
@@ -99,7 +100,7 @@
 
 # Widget implementation:
 
-class ViewSupport:
+class ViewSupport(object):
     """Helper class for vocabulary and vocabulary-query widgets."""
 
     def textForValue(self, term):
@@ -133,25 +134,29 @@
 
     extra = ""
     type = "vocabulary"
+    context = None
 
     def __init__(self, context, request):
         self.request = request
-        self.context = None
-
-    def _getDefault(self):
-        # Override this since the context is not the field for
-        # vocabulary-based widgets.
-        return self.context.default
+        self.vocabulary = context
+        # self.context is set to the field in setField below
 
     def setField(self, field):
         assert self.context is None
         # only allow this to happen for a bound field
         assert field.context is not None
         self.context = field
-        self.name = self._prefix + field.__name__
+        self.setPrefix(self._prefix)
 
     def __call__(self):
-        return self.render(self.getData(True))
+        if self._data is None:
+            if self.haveData():
+                data = self.getData(True)
+            else:
+                data = self._getDefault()
+        else:
+            data = self._data
+        return self.render(data)
 
     def render(self, value):
         raise NotImplementedError(
@@ -171,41 +176,46 @@
                 L.append(term.value)
         return L
 
-    # The *Data() methods have tightly bound semantics.  Subclasses
-    # need to be really careful about dealing with these, and should
-    # enlist this version for help whenever possible to make sure
-    # internal state is maintained.
-
     _have_field_data = False
 
-    def getData(self, optional=0):
-        data = getattr(self, "_field_data", self)
-        if data is self:
-            data = self._compute_data(self)
+    def getData(self, optional=False):
+        data = self._compute_data()
+        field = self.context
+        if data is None:
+            if field.required and not optional:
+                raise MissingInputError(field.__name__, field.title,
+                                        'the field is required')
+            return self._getDefault()
+        elif not optional:
+            try:
+                field.validate(data)
+            except ValidationError, v:
+                raise WidgetInputError(self.context.__name__,
+                                       self.title, str(v))
         return data
 
+    def setPrefix(self, prefix):
+        super(VocabularyWidgetBase, self).setPrefix(prefix)
+        # names for other information from the form
+        self.empty_marker_name = self.name + "-empty-marker"
+
+    def _emptyMarker(self):
+        return "<input name='%s' type='hidden' value='1' />" % (
+            self.empty_marker_name)
+
     def haveData(self):
-        self.getData()
-        return self._have_field_data
+        return (self.name in self.request.form or
+                self.empty_marker_name in self.request.form)
 
     def setData(self, value):
-        self._field_data = value
-        self._have_field_data = True
+        self._data = value
 
-    def _compute_data(self, optional):
+    def _compute_data(self):
         raise NotImplementedError(
             "_compute_data() must be implemented by a subclass\n"
             "It may be inherited from the mix-in classes SingleDataHelper\n"
             "or MultiDataHelper (from zope.app.browser.form.vocabularywidget)")
 
-    def _setup_default_data(self, optional):
-        # not on the content object either
-        if self.context.required and not optional:
-            raise MissingInputError(self.context.__name__,
-                                    self.title,
-                                    "required field not present")
-        return self._getDefault()
-
     def _showData(self):
         raise NotImplementedError(
             "vocabulary-based widgets don't use the _showData() method")
@@ -218,39 +228,23 @@
         raise NotImplementedError(
             "vocabulary-based widgets don't use the _unconvert() method")
 
-class SingleDataHelper:
+class SingleDataHelper(object):
 
-    def _compute_data(self, optional):
+    def _compute_data(self):
         if self.name in self.request.form:
             token = self.request.form[self.name]
-            data = self.convertTokensToValues([token])[0]
-            self.setData(data)
-            return data
-        data = self.context.query(self.context.context, self)
-        if data is self:
-            data = self._setup_default_data(optional)
-        else:
-            self.setData(data)
-        return data
+            return self.convertTokensToValues([token])[0]
+        return None
 
-class MultiDataHelper:
+class MultiDataHelper(object):
 
-    def _compute_data(self, optional):
+    def _compute_data(self):
         if self.name in self.request.form:
             tokens = self.request.form[self.name]
             if not isinstance(tokens, list):
                 tokens = [tokens]
-            data = self.convertTokensToValues(tokens)
-            self.setData(data)
-            return data
-        data = self.context.query(self.context.context, self)
-        if data is self:
-            data = self._setup_default_data(optional)
-        else:
-            data = []
-            self.setData(data)
-        return data
-
+            return self.convertTokensToValues(tokens)
+        return []
 
 class VocabularyDisplayWidget(SingleDataHelper, VocabularyWidgetBase):
     """Simple single-selection display that can be used in many cases."""
@@ -302,7 +296,7 @@
         return L
 
 
-class ActionHelper:
+class ActionHelper(object):
     __actions = None
 
     def addAction(self, action, msgid):
@@ -354,7 +348,7 @@
             queryview.setName(self.name + "-query")
 
     def setPrefix(self, prefix):
-        VocabularyWidgetBase.setPrefix(self, prefix)
+        super(VocabularyEditWidgetBase, self).setPrefix(prefix)
         if self.queryview is not None:
             self.queryview.setName(self.name + "-query")
 
@@ -369,6 +363,7 @@
                 contents.append(self._div('queryinput', s))
                 have_results = True
         contents.append(self._div('value', self.renderValue(value)))
+        contents.append(self._emptyMarker())
         if self.queryview and not have_results:
             s = self.queryview.renderInput()
             if s:
@@ -576,6 +571,8 @@
                                "More")
     _msg_no_results = _message(_("vocabulary-query-message-no-results"),
                                "No Results")
+    _msg_results_header = _message(_("vocabulary-query-header-results"),
+                                  "Search results")
 
     def setName(self, name):
         VocabularyQueryViewBase.setName(self, name)
@@ -658,6 +655,8 @@
         self.query_selections = QS
         return ''.join(
             ["<div class='results'>\n",
+             "<h4>%s</h4>\n" % (
+                 self.translate(self._msg_results_header)),
              self.makeSelectionList(items, self.query_selections_name),
              "\n",
              self.renderAction(ADD_DONE), "\n",
@@ -700,3 +699,4 @@
 
     def makeSelectionList(self, items, name):
         return self.mkselectionlist("checkbox", items, name)
+