[Zope3-dev] Mail delivery failed: returning message to sender

Mail Delivery System Mailer-Daemon at python.org
Tue Feb 17 07:54:41 EST 2004


This message was created automatically by mail delivery software.

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

  corey at streamreel.net
    SMTP error from remote mailer after RCPT TO:<corey at streamreel.net>:
    host iris1.directnic.com [204.251.10.81]: 550 5.7.1 No such recipient

------ This is a copy of the message, including all the headers. ------

Return-path: <zope3-dev at zope.org>
Received: from cache1.zope.org ([12.155.117.38])
	by mail.python.org with esmtp (Exim 4.22)
	id 1At4jf-0007dB-KX; Tue, 17 Feb 2004 07:53:59 -0500
From: zope3-dev at zope.org (srichter)
Reply-To: zope3-dev at zope.org
To: ;
Subject: [VocabulariesAndFields] (new) first draft (generated from LaTeX file)
Message-ID: <20040217075359EST at dev.zope.org>
X-BeenThere: zope3-dev at zope.org
X-Zwiki-Version: 0.21.1
Precedence: bulk
List-Id:  <zope3-dev at zope.org>
List-Post: <mailto:zope3-dev at zope.org>
List-Subscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/VocabulariesAndFields/subscribeform>
List-Unsubscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/VocabulariesAndFields/subscribeform>
List-Archive: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/VocabulariesAndFields>
List-Help: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture>
Date: Tue, 17 Feb 2004 07:53:59 -0500
X-Spam-Status: OK (default 0.000)

Vocabularies and their Fields

  Status

    IsDraft

  Authors

    StephanRichter

  Difficulty

    Sprinter Level  

  Skills

    - Be familiar with the 'schema' package, including widgets.  


  Problem/Task

    You will agree that schemas in combination with widgets and forms
    are pretty cool. The times of writing boring HTML forms and data
    verification are over. However, the standard fields make it hard (if not
    impossible) to create a dynamic list of possible values to choose from.
    To solve this problem, the vocabulary, vocabulary field and corresponding
    widgets were developed. In this recipe we will demonstrate one of the
    common usages of vocabularies in Zope 3.  


  Recipe

    
  Introduction


    A common user interface pattern is to provide the user with a list
    of available or possible values from which one or more might be selected.
    This is done to reduce the amount of errors the user could possibly make.
    Often the list of choices is static, meaning they do not change over time
    or are dependent on a particular situations. On the other hand, you
    commonly have choices for a field that depend strongly on the situation
    you are presented with.

    Standard enumerated fields do not have a mechanism to allow for
    dynamic choices. It would also be hard for them to create dynamic
    choices, since they are not aware of the context in which they are used.
    To address this problem, vocabularies were created, which are able to
    provide dynamic lists. The developer can then use a 'VocabularyField' 
    in her/his schema that specifies a specific vocabulary that should be
    used.

    Vocabularies in itself are a pretty complex subject, since
    implementations can range from providing the values of a simple list to
    backend SQL queries. For large data sets vocabularies also have a simple
    query support, so that we can build a sane user interface for the data.
    Generally, there are two ways to use vocabularies in Zope 3, the ones
    that do not and others that do need a place to generate the data.
    Vocabularies that do not need a place can be created as singeltons and
    would be useful when data is retrieved from a file, RDB or any other
    Zope-external data source. In this recipe, however, we are going to
    implement a vocabulary that provides a list of all the items of a
    container (or any other 'IReadMapping' object), so that the location
    matters.

    Vocabularies that need a location, cannot exist as singletons, but
    the location must be passed into the constructor. Zope 3 provides a
    vocabulary registry with which one can register vocabulary factories
    (which are usually just the classes) by name. The ZCML looks like this::

      01 <vocabulary
      02    name="VocabularyName"
      03    factory=".vocab.Vocabulary" />

    You can then use the vocabulary in a schema by declaring a
    vocabulary field::

      01 field = VocabularyField(
      02     title=u"...",
      03     description=u"...",
      04     vocabulary="VocabularyName")

    If the 'vocabulary' argument value is a string, then it is used as a
    vocabulary name, and the vocabulary is created with a context whenever
    needed. But the argument also accepts 'IVocabulary' instances, which
    are directly used.

    Okay, I wrote already too much. Let's see how we can achieve our
    task using vocabularies.

    
  The Vocabulary and its Terms


    A vocabulary has a very simple interface. It is almost like a simple
    mapping objects with some additional functionality. The main idea is that
    a vocabulary provides 'ITerm' objects. A term has simply a 'value' 
    that can be any Python object. However, for Web forms (and other user
    interfaces) this minimalistic interface does not suffice, since we have
    now way of reliably specifiying a unique id (a string) for a term, which
    we need to do to create any HTML input element with the terms. To solve
    this problem, the 'ITokenizedTerm' was developed, which provides a 'token' 
    attribute that must be a string identifying the term.

    However, since our vocabulary deals with folder item names, our 'ITerm''value' 
    is equal to the 'token' . Therefore, we only need a minimal
    implementation of 'ITokenizedTerm' as seen below.::

      01 from zope.interface import implements
      02 from zope.schema.interfaces import ITokenizedTerm
      03 from zope.interface.common.mapping import IEnumerableMapping
      05 class ItemTerm(object):
      06     """A simple term implementation for items."""
      07     implements(ITokenizedTerm)
      08     def __init__(self, value):
      09         self.value = self.token = value

    Create a new product called 'itemvocabulary' in 'ZOPE3/src/zope/products/demo' 
    . Place the above code in the '_' file.

    Next we need to implement the vocabulary. Since the context of the
    vocabulary is an 'IReadMapping' object, the implementation is
    straightforward::

      01 from zope.schema.interfaces import IVocabulary, IVocabularyTokenized
      02 from zope.interface.common.mapping import IEnumerableMapping
      04 class ItemVocabulary(object):
      05     """A vocabulary that provides the keys of any IEnumerableMapping object.
      07     Every dictionary will qualify for this vocabulary."""
      08     implements(IVocabulary, IVocabularyTokenized)
      09     __used_for__ = IEnumerableMapping
      11     def __init__(self, context):
      12         self.context = context
      14     def __iter__(self):
      15         """See zope.schema.interfaces.IIterableVocabulary"""
      16         return iter([ItemTerm(key) for key in self.context.keys()])
      18     def __len__(self):
      19         """See zope.schema.interfaces.IIterableVocabulary"""
      20         return len(self.context)
      22     def __contains__(self, value):
      23         """See zope.schema.interfaces.IBaseVocabulary"""
      24         return value in self.context.keys()
      26     def getQuery(self):
      27         """See zope.schema.interfaces.IBaseVocabulary"""
      28         return None
      30     def getTerm(self, value):
      31         """See zope.schema.interfaces.IBaseVocabulary"""
      32         if value not in self.context.keys():
      33             raise LookupError, value
      34         return ItemTerm(value)
      36     def getTermByToken(self, token):
      37         """See zope.schema.interfaces.IVocabularyTokenized"""
      38         return self.getTerm(token)

    o Line 8: Make sure that you implement both, 'IVocabulary' and 'IVocabularyTokenized' 
      , so that the widget mechanism will work correctly later.

    o Line 14-16: Make sure that the values of the iterator are 'ITerm' 
      objects and not simple strings.

    o Line 26-28: We do not support queries in this implementation.
      The interface specifies that vocabularies not supporting queries must
      return 'None' .

    o Line 30-34: We must be careful here and not just create an 'ItemToken' 
      from the value, since the interface specifies that if the value is not
      available in the vocabulary, a 'LookupError' should be raised.

    o Line 36-38: Since the 'token' and the 'value' are equal, we can
      just forward the request to 'getTerm()' .  


    Since the vocabulary requires a context for initiation, we need to
    register it with the vocabulary registry. The vocabulary is also used in
    untrusted environments, so that we have to make security assertions for
    it and the term. Place the ZCML directives below in the 'configure.zcml' 
    of the package.::

      01 <configure
      02     xmlns="http://namespaces.zope.org/zope"
      03     i18n_domain="itemvocabulary">
      05 <vocabulary
      06     name="Items"
      07     factory=".ItemVocabulary" />
      09 <content class=".ItemVocabulary">
      10   <allow interface="zope.schema.interfaces.IVocabulary"/>
      11   <allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
      12 </content>
      14 <content class=".ItemTerm">
      15   <allow interface="zope.schema.interfaces.ITokenizedTerm"/>
      16   <allow attributes="title"/>
      17 </content>
      19 </configure>

    o Line 5-7: Register the vocabulary under the name "Items". The
      vocabulary directive is available in the default "zope" namespace.

    o Line 9-16: We simply open up all of the interfaces to the
      public, since the objects that provide the data are protected
      themselves.  


    That was easy, right? Now, let's write some quick tests for this
    code.

    
  Testing the Vocabulary


    The tests are as straightforward as the code itself. We are going to
    only test the vocabulary, since it uses the trivial term. In the doc
    string of the 'ItemVocabulary' class add the following example and
    test code::

      01 """
      02 Example::
      04   >>> data = {'a': 'Anton', 'b': 'Berta', 'c': 'Charlie'}
      05   >>> vocab = ItemVocabulary(data)
      06   >>> iterator = iter(vocab)
      07   >>> iterator.next().token
      08   'a'
      09   >>> len(vocab)
      10   3
      11   >>> 'c' in vocab
      12   True
      13   >>> vocab.getQuery() is None
      14   True
      15   >>> vocab.getTerm('b').value
      16   'b'
      17   >>> vocab.getTerm('d')
      18   Traceback (most recent call last):
      19   ...
      20   LookupError: d
      21   >>> vocab.getTermByToken('b').token
      22   'b'
      23   >>> vocab.getTermByToken('d')
      24   Traceback (most recent call last):
      25   ...
      26   LookupError: d
      27 """

    The tests are activated via a doc test that is initialized in 'tests.py' 
    with the following code::

      01 import unittest
      02 from zope.testing.doctestunit import DocTestSuite
      04 def test_suite():
      05     return unittest.TestSuite((
      06         DocTestSuite('zope.products.demo.itemvocabulary'),
      07         ))
      09 if __name__ == '__main__':
      10     unittest.main(defaultTest='test_suite')

    You can execute the tests as usual via the Zope 3 test runner or
    call the test file directly after you have set the correct Python path.

    
  The Default Item Folder


    To see the vocabulary working, we will develop a derived Folder,
    which simply keeps track of a default item (whatever "default" may mean).
    Since the folder is part of a browser demonstration, we write the folder
    interface and implementation in 'browser.py'::

      01 from zope.interface import implements, Interface
      02 from zope.schema.vocabulary import VocabularyField
      03 from zope.app.content.folder import Folder
      05 class IDefaultItem(Interface):
      07     default = VocabularyField(
      08         title=u"Default Item Key",
      09         description=u"Key of the default item in the folder.",
      10         vocabulary="Items")
      12 class DefaultItemFolder(Folder):
      13     implements(IDefaultItem)
      15     default = None

    o Line 7-10: The 'VocabularyField' is like any other field, except
      that you can specify a vocabulary. The 'vocabulary' argument can
      either be the vocabulary name or a vocabulary instance, as pointed out
      earlier in this recipe.

    o Line 12-15: A trivial content component implementation that
      combines 'IFolder' and 'IDefaultItem' .  


    Now we only have we just have to register the new content component,
    make some security assertions and create an edit form for the 'default' 
    value. All of this can be done with the following three ZCML directives::

      01 <content class=".browser.DefaultItemFolder">
      02   <require like_class="zope.app.content.folder.Folder"/>    
      04   <require
      05       permission="zope.View"
      06       interface=".browser.IDefaultItem" />
      08   <require
      09       permission="zope.ManageContent"
      10       set_schema=".browser.IDefaultItem" />
      11 </content>
      13 <browser:addMenuItem
      14     class=".browser.DefaultItemFolder"
      15     title="Default Item Folder"
      16     permission="zope.ManageContent" />
      18 <browser:editform
      19     schema=".browser.IDefaultItem"
      20     for=".browser.IDefaultItem"
      21     label="Change Default Item"
      22     name="defaultItem.html"
      23     permission="zope.ManageContent"
      24     menu="zmi_views" title="Default Item" />

    Don't forget to register the 'browser' namespace in the 'configure' 
    tag::

      xmlns:browser="http://namespaces.zope.org/browser"
        You are now ready to go. Restart Zope 3. Once you refresh the
    ZMI, you will see that you can now add a "Default Item Folder". Create
    such a folder and add a couple other components to it, like images and
    files. If you now click on the "Default Item" tab, you will see a
    selection box with the names of all contained objects. Select one and
    submit the form. You now stored the name of the object that will be
    considered the "default". As you can see, there exist widgets that know
    how to display a vocabulary field. See exercise 1 for changing the used
    widget.  


  Exercises

    - Exercise 1: Change the 'defaultItem.html' of the 'DefaultItemFolder' 
      so that it uses radio buttons instead of a drop-down menu. (Hint: You
      need to create a Python view class for the 'editform' directive
      and add a custom widget.)  

--
forwarded from http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/VocabulariesAndFields



More information about the Zope3-dev mailing list