[Zope3-checkins] CVS: Zope3/src/zope/app/form/browser - README.txt:1.1 __init__.py:1.4 configure.zcml:1.6 itemswidgets.py:1.3 sequencewidget.py:1.2

Gary Poster gary at zope.com
Thu May 6 12:14:11 EDT 2004


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

Modified Files:
	__init__.py configure.zcml itemswidgets.py sequencewidget.py 
Added Files:
	README.txt 
Log Message:
Convert the field collection behavior as described in 
http://mail.zope.org/pipermail/zope3-dev/2004-May/010797.html

The Sequence field is removed.  As I spoke with Stephan, it may be acceptable to add the Sequence field back in if it actually means something.  Sequence did mean it was iterable but container access API was not described.  It should be described.  IPythonSequence might describe this API.  A Sequence widget should require specification of the factory.

Set now specifies a sets.Set.

IChoiceSequence was removed.




=== Added File Zope3/src/zope/app/form/browser/README.txt ===
===============
Browser Widgets
===============

This directory contains widgets: views on bound schema fields.  Many of these
are straightforward.  For instance, see the TextWidget in textwidgets.py, which
is a subclass of BrowserWidget in widget.py.  It is registered as an
IBrowserRequest view of an ITextLine schema field, providing the IInputWidget
interface::

  <view
      type="zope.publisher.interfaces.browser.IBrowserRequest"
      for="zope.schema.interfaces.ITextLine"
      provides="zope.app.form.interfaces.IInputWidget"
      factory=".TextWidget"
      permission="zope.Public"
      />

The widget then receives the field and the request as arguments to the factory
(i.e., the TextWidget class).

Some widgets in Zope 3 extend this pattern.  This extension is configurable:
simply do not load the zope/app/form/browser/configure.zcml file if you do not
wish to participate in the extension.  The widget registration is extended for
Choice fields and for the collection fields.

Default Choice Field Widget Registration and Lookup
===================================================

As described above, all field widgets are obtained by looking up a browser
IInputWidget or IDisplayWidget view for the field object.  For Choice fields,
the default registered widget defers all of its behavior to the result of
another lookup: a browser widget view for the field *and* the Choice field's
vocabulary.  

This allows registration of Choice widgets that differ on the basis of the
vocabulary type.  For example, a widget for a vocabulary of images might have
a significantly different user interface than a widget for a vocabulary of
words.  A dynamic vocabulary might implement IIterableVocabulary if its
contents are below a certain length, but not implement the marker "iterable"
interface if the number of possible values is above the threshhold.

This also means that choice widget factories are called with with an additional
argument.  Rather than being called with the field and the request as
arguments, choice widgets receive the field, vocabulary, and request as
arguments.

Some Choice widgets may also need to provide a query interface, particularly if
the number of items in the vocabuary are too big to iterate.  The vocabulary
may provide a query which implements an interface appropriate for that
vocabulary.  You then can register a query view--a view registered for the
query interface and the field interface--that implements
zope.app.forms.browser.interfaces.IVocabularyQueryView.

Default Collection Field Widget Registration and Lookup
=======================================================

The default configured lookup for collection fields--List, Tuple, and Set, for
instance--begins with the usual lookup for a browser widget view for the
field object.  This widget defers its display to the result of another lookup:
a browser widget view registered for the field and the field's value_type (the
type of the contained values).  This allows registrations for collection
widgets that differ on the basis of the members--a widget for entering a list
of text strings might differ significantly from a widget for entering a list of
dates...or even a list of choices, as discussed below.

This registration pattern has three implications that should be highlighted. 

 * First, collection fields that do not specify a value_type probably cannot
   have a reasonable widget.  

 * Second, collection widgets that wish to be the default widget for a
   collection with any value_type should be registered for the collection
   field and a generic value_type: the IField interface.  Do  not register the
   generic widget for the collection field only or you will break the lookup
   behavior as described here.
 
 * Third, like choice widget factories, sequence widget factories (classes or
   functions) take three arguments.  Typical sequence widgets receive the
   field, the value_type, and the request as arguments.

Collections of Choices
----------------------

If a collection field's value_type is a Choice field, the second widget again
defers its behavior, this time to a third lookup based on the collection field
and the choice's vocabulary.  This means that a widget for a list of large
image choices can be different than a widget for a list of small image choices
(with a different vocabulary interface), different from a widget for a list of
keyword choices, and different from a set of keyword choices.

Some advanced applications may wish to do a further lookup on the basis of the
unique attribute of the collection field--perhaps looking up a named view with
a "unique" or "lenient" token depending on the field's value, but this is not
enabled in the default Zope 3 configuration.

Registering Widgets for a New Collection Field Type
---------------------------------------------------

Because of this lookup pattern, basic widget registrations for new field types
must follow a recipe.  For example, a developer may introduce a new Bag field
type for simple shopping cart functionality and wishes to add widgets for it
within the default Zope 3 collection widget registration.  The bag widgets
should be registered something like this. 

The only hard requirement is that the developer must register the bag + choice
widget: the widget is just the factory for the third dispatch as described
above, so the developer can use the already implemented widgets listed below.

  <view
      type="zope.publisher.interfaces.browser.IBrowserRequest"
      for="zope.schema.interfaces.IBag
           zope.schema.interfaces.IChoice"
      provides="zope.app.form.interfaces.IDisplayWidget"
      factory=".ChoiceCollectionDisplayWidget"
      permission="zope.Public"
      />

  <view
      type="zope.publisher.interfaces.browser.IBrowserRequest"
      for="zope.schema.interfaces.IBag
           zope.schema.interfaces.IChoice"
      provides="zope.app.form.interfaces.IInputWidget"
      factory=".ChoiceCollectionInputWidget"
      permission="zope.Public"
      />

Beyond this, the developer may also have a generic bag widget she wishes to
register.  This might look something like this, assuming there's a
BagSequenceWidget available in this package::

  <view
      type="zope.publisher.interfaces.browser.IBrowserRequest"
      for="zope.schema.interfaces.IBag
           zope.schema.interfaces.IField"
      provides="zope.app.form.interfaces.IInputWidget"
      factory=".BagSequenceWidget"
      permission="zope.Public"
      />

Then any widgets for the bag and a vocabulary would be registered according to
this general pattern, in which IIterableVocabulary would be the interface of
any appropriate vocabulary and BagWidget is some appropriate widget::

  <view
      type="zope.publisher.interfaces.browser.IBrowserRequest"
      for="zope.schema.interfaces.IBag
           zope.schema.interfaces.IIterableVocabulary"
      provides="zope.app.form.interfaces.IInputWidget"
      factory=".BagWidget"
      permission="zope.Public"
      />


=== Zope3/src/zope/app/form/browser/__init__.py 1.3 => 1.4 ===
--- Zope3/src/zope/app/form/browser/__init__.py:1.3	Sat Apr 24 19:19:42 2004
+++ Zope3/src/zope/app/form/browser/__init__.py	Thu May  6 12:13:41 2004
@@ -39,9 +39,11 @@
 # Widgets for fields with vocabularies.
 # Note that these are only dispatchers for the widgets below.
 from zope.app.form.browser.itemswidgets import ChoiceDisplayWidget
-from zope.app.form.browser.itemswidgets import ChoiceSequenceDisplayWidget
-from zope.app.form.browser.itemswidgets import ChoiceEditWidget
-from zope.app.form.browser.itemswidgets import ChoiceSequenceEditWidget
+from zope.app.form.browser.itemswidgets import ChoiceInputWidget
+from zope.app.form.browser.itemswidgets import CollectionDisplayWidget
+from zope.app.form.browser.itemswidgets import CollectionInputWidget
+from zope.app.form.browser.itemswidgets import ChoiceCollectionDisplayWidget
+from zope.app.form.browser.itemswidgets import ChoiceCollectionInputWidget
 
 # Widgets that let you choose a single item from a list
 # These widgets are multi-views on (field, vocabulary)


=== Zope3/src/zope/app/form/browser/configure.zcml 1.5 => 1.6 ===
--- Zope3/src/zope/app/form/browser/configure.zcml:1.5	Mon May  3 22:01:24 2004
+++ Zope3/src/zope/app/form/browser/configure.zcml	Thu May  6 12:13:41 2004
@@ -131,60 +131,130 @@
 
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.ITuple"
+      for="zope.schema.interfaces.IPassword"
       provides="zope.app.form.interfaces.IInputWidget"
-      factory=".TupleSequenceWidget"
+      factory=".PasswordWidget"
       permission="zope.Public"
       />
 
+  <!-- Items-related widgets; they are proxies for the multiviews below. -->
+
+  <!-- Choices (dispatch to field + vocabulary lookup) -->
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IList"
+      for="zope.schema.interfaces.IChoice"
+      provides="zope.app.form.interfaces.IDisplayWidget"
+      factory=".ChoiceDisplayWidget"
+      permission="zope.Public"
+      />
+
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.IChoice"
       provides="zope.app.form.interfaces.IInputWidget"
-      factory=".ListSequenceWidget"
+      factory=".ChoiceInputWidget"
       permission="zope.Public"
       />
 
+  <!-- Generic collections (dispatch to field + value_type lookup) -->
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IPassword"
+      for="zope.schema.interfaces.ICollection"
+      provides="zope.app.form.interfaces.IDisplayWidget"
+      factory=".CollectionDisplayWidget"
+      permission="zope.Public"
+      />
+
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.ICollection"
       provides="zope.app.form.interfaces.IInputWidget"
-      factory=".PasswordWidget"
+      factory=".CollectionInputWidget"
       permission="zope.Public"
       />
 
+  <!-- non-choice collection fields should register for the field + value type
+       so as to allow specific field + value_type widgets such as the Choice
+       pattern -->
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.ITuple
+           zope.schema.interfaces.IField"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".TupleSequenceWidget"
+      permission="zope.Public"
+      />
 
-  <!-- Items-related widgets; they are proxies for the multiviews below. -->
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.IList
+           zope.schema.interfaces.IField"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".ListSequenceWidget"
+      permission="zope.Public"
+      />
 
+  <!-- Choice collections.  dispatch to field + vocabulary lookup.
+       We must register the collection + choice factories for all ICollection
+       subclasses because the field (the collection) has precedence: therefore
+       a registration for (IList plus IField) would trump a registration for
+       (ICollection plus IChoice), making choice lists not look up properly.
+       So all new collection types should always register for IChoice if
+       they want to follow in this configuration pattern. -->
+  <!-- List + Choice -->
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IChoice"
+      for="zope.schema.interfaces.IList
+           zope.schema.interfaces.IChoice"
       provides="zope.app.form.interfaces.IDisplayWidget"
-      factory=".ChoiceDisplayWidget"
+      factory=".ChoiceCollectionDisplayWidget"
       permission="zope.Public"
       />
 
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IChoice"
+      for="zope.schema.interfaces.IList
+           zope.schema.interfaces.IChoice"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".ChoiceCollectionInputWidget"
+      permission="zope.Public"
+      />
+
+  <!-- Tuple + Choice -->
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.ITuple
+           zope.schema.interfaces.IChoice"
+      provides="zope.app.form.interfaces.IDisplayWidget"
+      factory=".ChoiceCollectionDisplayWidget"
+      permission="zope.Public"
+      />
+
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.ITuple
+           zope.schema.interfaces.IChoice"
       provides="zope.app.form.interfaces.IInputWidget"
-      factory=".ChoiceEditWidget"
+      factory=".ChoiceCollectionInputWidget"
       permission="zope.Public"
       />
 
+  <!-- Set + Choice -->
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IChoiceSequence"
+      for="zope.schema.interfaces.ISet
+           zope.schema.interfaces.IChoice"
       provides="zope.app.form.interfaces.IDisplayWidget"
-      factory=".ChoiceSequenceDisplayWidget"
+      factory=".ChoiceCollectionDisplayWidget"
       permission="zope.Public"
       />
 
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IChoiceSequence"
+      for="zope.schema.interfaces.ISet
+           zope.schema.interfaces.IChoice"
       provides="zope.app.form.interfaces.IInputWidget"
-      factory=".ChoiceSequenceEditWidget"
+      factory=".ChoiceCollectionInputWidget"
       permission="zope.Public"
       />
 
@@ -208,9 +278,11 @@
       permission="zope.Public"
       />
 
+  <!-- These widgets are minimal and only support lists with unique members,
+       without ordering capabilities -->
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IChoiceSequence
+      for="zope.schema.interfaces.IList
            zope.schema.interfaces.IIterableVocabulary"
       provides="zope.app.form.interfaces.IInputWidget"
       factory=".MultiSelectWidget"
@@ -219,14 +291,14 @@
 
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
-      for="zope.schema.interfaces.IChoiceSequence
+      for="zope.schema.interfaces.IList
            zope.schema.interfaces.IIterableVocabulary"
       provides="zope.app.form.interfaces.IDisplayWidget"
       factory=".SetDisplayWidget"
       permission="zope.Public"
       />
 
-  <!-- Register a couple default vocabulary query views. -->
+  <!-- Register a couple of default vocabulary query views. -->
 
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
@@ -240,7 +312,7 @@
   <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
       for="zope.schema.interfaces.IVocabularyQuery
-           zope.schema.interfaces.IChoiceSequence"
+           zope.schema.interfaces.ICollection"
       provides="zope.app.form.browser.interfaces.IVocabularyQueryView"
       factory=".vocabularyquery.IterableVocabularyQueryMultiView"
       permission="zope.Public"


=== Zope3/src/zope/app/form/browser/itemswidgets.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/form/browser/itemswidgets.py:1.2	Sat Apr 24 19:19:42 2004
+++ Zope3/src/zope/app/form/browser/itemswidgets.py	Thu May  6 12:13:41 2004
@@ -19,7 +19,7 @@
 from zope.i18n import translate
 from zope.proxy import removeAllProxies
 from zope.schema.interfaces import ValidationError, InvalidValue
-from zope.schema.interfaces import ConstraintNotSatisfied
+from zope.schema.interfaces import ConstraintNotSatisfied, ITitledTokenizedTerm
 
 from zope.app import zapi
 from zope.app.form.browser.widget import BrowserWidget, renderElement
@@ -29,26 +29,39 @@
 from zope.app.i18n import ZopeMessageIDFactory as _
 
 
-# For fields with vocabularies, we really want to make the widget a view
-# of the field and vocabulary.
+# For choices, we want to make the widget a view of the field and vocabulary.
 
 def ChoiceDisplayWidget(field, request):
     return zapi.getMultiView((field, field.vocabulary), request,
                              IDisplayWidget)
 
-def ChoiceSequenceDisplayWidget(field, request):
-    return zapi.getMultiView((field, field.value_type.vocabulary), request,
-                             IDisplayWidget)
-
-def ChoiceEditWidget(field, request):
+def ChoiceInputWidget(field, request):
     return zapi.getMultiView((field, field.vocabulary), request,
                              IInputWidget)
 
-def ChoiceSequenceEditWidget(field, request):
-    return zapi.getMultiView((field, field.value_type.vocabulary), request,
+# for collections, we want to make the widget a view of the field and the 
+# value_type.  If the value_type is None we may fall over.  We may
+# not be able to do any better than that.
+
+def CollectionDisplayWidget(field, request):
+    return zapi.getMultiView((field, field.value_type), request,
+                             IDisplayWidget)
+
+def CollectionInputWidget(field, request):
+    return zapi.getMultiView((field, field.value_type), request,
                              IInputWidget)
-    
-    
+
+# for collections of choices, we want to make the widget a view of the field, 
+# the value type, and the vocabulary.
+
+def ChoiceCollectionDisplayWidget(field, value_type, request):
+    return zapi.getMultiView(
+        (field, value_type.vocabulary), request, IDisplayWidget)
+
+def ChoiceCollectionInputWidget(field, value_type, request):
+    return zapi.getMultiView(
+        (field, value_type.vocabulary), request, IInputWidget)
+
 class TranslationHook(object):
     """A mixin class that provides the translation capabilities."""
 
@@ -94,6 +107,8 @@
         This can be overridden to support more complex term objects. The token
         is returned here since it's the only thing known to be a string, or
         str()able."""
+        if ITitledTokenizedTerm.providedBy(term):
+            return term.title
         return term.token
 
     def convertTokensToValues(self, tokens):
@@ -305,7 +320,8 @@
         super(ItemsEditWidgetBase, self).__init__(field, vocabulary, request)
 
         # Queries are used in items widgets to reduce the amount of choices,
-        # since some vocabularies could literally provide thousands of terms.
+        # since some vocabularies could literally provide an infinite number
+        # of terms.
         self.queryview = None
         query = vocabulary.getQuery()
         if query is not None:


=== Zope3/src/zope/app/form/browser/sequencewidget.py 1.1 => 1.2 ===
--- Zope3/src/zope/app/form/browser/sequencewidget.py:1.1	Wed Mar 17 12:35:02 2004
+++ Zope3/src/zope/app/form/browser/sequencewidget.py	Thu May  6 12:13:41 2004
@@ -36,7 +36,7 @@
     _type = tuple    
     _data = () # pre-existing sequence items (from setRenderedValue)
 
-    def __init__(self, context, request, subwidget=None):
+    def __init__(self, context, value_type, request, subwidget=None):
         super(SequenceWidget, self).__init__(context, request)
 
         self.subwidget = None




More information about the Zope3-Checkins mailing list