[Checkins] SVN: zope3book/trunk/source/ Added Schema chapter

Baiju M baiju.m.mail at gmail.com
Sat Feb 28 08:40:02 EST 2009


Log message for revision 97382:
  Added Schema chapter
  

Changed:
  U   zope3book/trunk/source/index.rst
  A   zope3book/trunk/source/schema.rst

-=-
Modified: zope3book/trunk/source/index.rst
===================================================================
--- zope3book/trunk/source/index.rst	2009-02-28 12:26:20 UTC (rev 97381)
+++ zope3book/trunk/source/index.rst	2009-02-28 13:40:01 UTC (rev 97382)
@@ -17,6 +17,7 @@
    browser-pages
    content-components
    skinning
+   schema
 
 
 Indices and tables

Added: zope3book/trunk/source/schema.rst
===================================================================
--- zope3book/trunk/source/schema.rst	                        (rev 0)
+++ zope3book/trunk/source/schema.rst	2009-02-28 13:40:01 UTC (rev 97382)
@@ -0,0 +1,600 @@
+Zope Schemas and Widgets
+========================
+
+
+Introduction
+------------
+
+In the early stages of development, the Zope 3 developers decided
+that it would be cumbersome to manually write HTML forms and to
+manually validate the input.  Zope 3 team realized that if they could
+extend interfaces, we could auto-generate HTML forms and also
+automatically validate any input.  This chapter gives some background
+information and formally introduces the ``zope.schema`` and
+``zope.app.form`` packages.
+
+
+History and Motivation
+----------------------
+
+Originally, I simply wanted to port Formulator, a very successful
+Zope 2 product to auto-generate and validate forms, to Zope 3.  In
+Formulator, one would create various input fields (like integers or
+text lines) in a form and provide some meta-data about the fields,
+like the maximum and minimum length of a string.  You could then tell
+the form to simply render itself.  For more details see
+`http://zope.org/Members/infrae/Formulator`_.
+
+Even though Formulator tried to split application logic and
+presentation, various parts were still not sufficiently separated,
+mainly due to the limitations Zope 2 provided.  Therefore the
+original port remained a hack in Zope 3 until the idea of schemas was
+developed by Jim Fulton and Martijn Faassen (the original author of
+Formulator) during the Berlin BBQ Sprint (April 2002) when trying to
+combine Zope 3's version of Formulator and class properties.  After
+all presentation logic was removed, Formulator fields felt a lot like
+interface specification for attributes.  So it was realized, that if
+we supply more meta-data to the attribute declarations in interfaces,
+then we could accomplish auto-generation and validation of HTML
+forms.  These extended attributes are still known as "fields".  If an
+interface contains any fields, then this interface is conventionally
+called a schema.
+
+The following three main goals of schemas developed:
+
+1.  Full specification of properties on an API level
+
+2.  Data input validation and conversion
+
+3.  Automated GUI form generation (mainly for the Web browser)
+
+
+Schema versus Interfaces
+------------------------
+
+As mentioned before, schemas are just an extension to interfaces and
+therefore depend on the zope.interface package.  Fields in schemas
+are equivalent to methods in interfaces.  Both are complementary to
+each other, since they describe different aspects of an object.  The
+methods of an interface describe the functionality of a component,
+while the schema's fields represent the state.
+
+It is thus not necessary to develop a new syntax for writing schemas
+and we simply reuse the interface declaration::
+
+  from zope.interface import Interface
+  from zope.schema import Text
+
+  class IExample(Interface):
+
+      text = Text(
+          title=u"Text",
+          description=u"The text of the example.",
+          required=True)
+
+- Line 2: All default fields can be simply imported from zope.schema.
+
+- Line 7-8: The title and description are used as human-readable text
+  for the form generation. Of course, they also serve as
+  documentation of the field itself.
+
+- Line 9: Various fields support several other meta-data fields.  The
+  required option is actually available for all fields and specifies
+  whether an object implementing IExample must provide a value for
+  text or not.
+
+
+Core Schema Fields
+------------------
+
+After we have seen a simple example of a schema, let's now look at all the
+basic fields and their properties.
+
+-   Properties that all fields support:
+
+    - title (type: TextLine): The title of the attribute is used as
+      label when displaying the field widget.
+
+    - description (type: Text): The description of the attribute is
+      used for tooltips and advanced help.
+
+    - required (type: Bool): Specifies whether an attribute is
+      required or not to have a value.  In add-forms, required
+      attributes are equivalent to required constructor arguments.
+
+    - readonly (type: Bool): If a field is readonly, then the value
+      of the attribute can be set only once and can then only be
+      displayed.  Often a unique id for some object is a good
+      candidate for a read-only field.
+
+    - default (type: depends on field): The default value that is
+      given to the attribute, if no initialization value was
+      provided.  This value is often specified, if a field is
+      required.
+
+    - order (type: Int): Fields are often grouped by some logical
+      order.  This value specifies a relative position in this order.
+      We usually do not set this value manually, since it is
+      automatically assigned when an interface is initialized.  The
+      order of the fields in a schema is by default the same as the
+      order of the fields in the Python code.
+
+-   Bytes, BytesLine
+
+    Bytes and BytesLine only differ by the fact that BytesLine cannot
+    contain a new line character.  Bytes behave identical to the
+    Python type str.
+
+    Bytes and BytesLine fields are iteratable.
+
+    - min_length (type: Int): After the white space has been
+      normalized, there cannot be less than this amount of characters
+      in the bytes string.  The default is None, which refers to no
+      minimum.
+
+    - max_length (type: Int): After the white space has been
+      normalized, there cannot be more than this amount of characters
+      in the bytes string.  The default is None, which refers to no
+      maximum.
+
+-   Text, TextLine
+
+    The two fields only differ by the fact that TextLine cannot
+    contain a newline character.  Text fields contain unicode,
+    meaning that they are intended to be human-readable strings/text.
+
+    Text and TextLine fields are iteratable.
+
+    - min_length (type: Int): After the white space has been
+      normalized, there cannot be less than this amount of characters
+      in the text string.  The default is None, which refers to no
+      minimum.
+
+    - max_length (type: Int): After the white space has been
+      normalized, there cannot be more than this amount of characters
+      in the text string.  The default is None, which refers to no
+      maximum.
+
+-   SourceText
+
+    Source Text is a special field derived from Text, which contains
+    source code of any type.  It is more or less a marker field for
+    the forms machinery, so that special input fields can be used for
+    source code.
+
+-   Password
+
+    Password is a special derivative for the TextLine field and is
+    treated separately for presentation reasons.  However, someone
+    also might want more fine-grained validation for passwords.
+
+-   Bool
+
+    The Bool field has no further attributes.  It maps directly to
+    Python's bool object.
+
+-   Int
+
+    Int fields directly map to Python's int type.
+
+    - min (type: Int): Specifies the smallest acceptable integer.
+      This is useful in many ways, such as allowing only positive
+      values by making this field 0.
+
+    - max (type: Int): Specifies the largest acceptable integer,
+      which excludes the value itself.  It can be used to specify an
+      upper bound, such as the current year, if you are interested in
+      the past only.
+
+    Both attributes combined allow the programmer to specify ranges
+    of acceptable values.
+
+-   Float
+
+    Float fields directly map to Python's float type.
+
+    - min (type: Float): Specifies the smallest acceptable floating
+      point number.  This is useful in many ways, such as allowing
+      only positive values by making this field 0.0.
+
+    - max (type: Float): Specifies the largest acceptable floating
+      point number, which excludes the value itself (typical computer
+      programming pattern).  It can be used to specify an upper
+      bound, such as 1.0, if you are only interested in
+      probabilities.
+
+    Both attributes combined allow the programmer to specify ranges of
+    acceptable values.
+
+-   Datetime
+
+    Similar to Int and Float, Datetime has a min and max field that
+    specify the boundaries of the possible values.  Acceptable values
+    for these fields must be instances of the builtin datetime type.
+
+-   Tuple, List
+
+    The reason both of these fields exists is that we can easily map them
+    to their Python type tuple and list, respectively.
+
+    Tuple and List fields are iteratable.
+
+    - min_length (type: Int): There cannot be less than this amount
+      of items in the sequence.  The default is None, which means
+      there is no minimum.
+
+    - max_length (type: Int): There cannot be more than this amount
+      of items in the sequence.  The default is None, which means
+      there is no maximum.
+
+    - value_type (type: Field): Values contained by these sequence
+      types must conform to this field's constraint.  Most commonly a
+      Choice field (see below) is specified here, which allows you to
+      select from a fixed set of values.
+
+-   Dict
+
+    The Dict is a mapping field that maps from one set of fields to
+    another.
+
+    fields are iteratable.
+
+    - min_length (type: Int): There cannot be less than this amount
+      of items in the dictionary.  The default is None, which means
+      there is no minimum.
+
+    - max_length (type: Int): There cannot be more than this amount
+      of items in the dictionary.  The default is None, which means
+      there is no maximum.
+
+    - key_type (type: Field): Every dictionary item key has to
+      conform to the specified field.
+
+    - value_type (type: Field): Every dictionary item value has to
+      conform to the specified field.
+
+-   Choice
+
+    The Choice field allows one to select a particular value from a
+    provided set of values.  You can either provide the values as a
+    simple sequence (list or tuple) or specify a vocabulary (by
+    reference or name) that will provide the values.  Vocabularies
+    provide a flexible list of values, in other words the set of
+    allowed values can change as the system changes.  Since they are
+    so complex, they are covered separately in "Vocabularies and
+    Fields".
+
+    - vocabulary (type: Vocabulary): A vocabulary instance that is
+      used to provide the available values.  This attribute is None,
+      if a vocabulary name was specified and the field has not been
+      bound to a context.
+
+    - vocabularyName (type: TextLine): The name of the vocabulary
+      that is used to provide the values.  The vocabulary for this
+      name can only be looked up, when the field is bound, in other
+      words has a context.  Upon binding, the vocabulary is
+      automatically looked using the name and the context.
+
+    The constructor also accepts a values argument that specifies a
+    static set of values.  These values are immediately converted to
+    a static vocabulary.
+
+-   Object
+
+    This field specifies an object that must implement a specific
+    schema.  Only objects that provide the specified schema are
+    allowed.
+
+    - schema (type: Interface): This field provides a reference to
+      the schema that must be provided by objects that want to be
+      stored in the described attribute.
+
+-   DottedName
+
+    Derived from the BytesLine field, the DottedName field represents
+    valid Python-style dotted names (object references).  This field
+    can be used when it is desirable that a valid and resolvable
+    Python dotted name is provided.
+
+    This field has no further attributes.
+
+-   URI
+
+    Derived from the BytesLine field, the URI field makes sure that
+    the value is always a valid URI.  This is particularly useful
+    when you want to reference resources (such as RSS feeds or
+    images) on remote computers.
+
+    This field has no further attributes.
+
+-   Id
+
+    Both, the DottedName and URI field, make up the Id field.  Any
+    dotted name or URI represent a valid id in Zope.  Ids are used
+    for identifying many types of objects, such as permissions and
+    principals, but also for providing annotation keys.
+
+    This field has no further attributes.
+
+-   InterfaceField
+
+    The Interface field has no further attributes.  Its value must be
+    an object that provides zope.interface.Interface, in other words
+    it must be an interface.
+
+For a formal listing of the Schema/Field API, see the API
+documentation tool at `http://localhost:8080/++apidoc++`_ or see
+zope.schema.interfaces module.
+
+
+Auto-generated Forms using the forms Package
+--------------------------------------------
+
+Forms are much more Zope-specific than schemas and can be found in
+the zope.app.forms package.  The views of schema fields are called
+widgets.  Widgets responsible for data display and conversion in
+their specific presentation type.  Currently widgets exist mainly for
+HTML (the Web browser).
+
+Widgets are separated into two groups, display and input widgets.
+Display widgets are often very simply and only show a text
+representation of the Python object.  The input widgets, however, are
+more complex and display a greater variety of choices.  The following
+list shows all available browser- based input widgets (found in
+zope.app.form.browser):
+
+
+Text Widgets
+~~~~~~~~~~~~
+
+Text-based widgets always require some sort of keyboard input.  A
+string representation of a field is then converted to the desired
+Python object, like and integer or a date.
+
+- TextWidget: Being probably the simplest widget, it displays the
+  text input element and is mainly used for the TextLine, which
+  expects to be unicode.  It also serves as base widget for many of
+  the following widgets.
+
+- TextAreaWidget: As the name suggests this widget displays a text
+  area and assumes its input to be some unicode string.  (note that
+  the Publisher already takes care of the encoding issues).
+
+- BytesWidget, BytesAreaWidget: Direct descendents from TextWidget
+  and TextAreaWidget, the only difference is that these widgets
+  expect bytes as input and not a unicode string, which means they
+  must be valid ASCII encodable.
+
+- ASCIIWidget: This widget, based on the BytesWidget, ensures that
+  only ASCII character are part of the inputted data.
+
+- PasswordWidget: Almost identical to the TextWidget, it only
+  displays a password element instead of a text element.
+
+- IntWidget: A derivative of TextWidget, it only overwrites the
+  conversion method to ensure the conversion to an integer.
+
+- FloatWidget: Derivative of TextWidget, it only overwrites the
+  conversion method to ensure the conversion to an floating point.
+
+- DatetimeWidget: Someone might expect a smart and complex widget at
+  this point, but for now it is just a simple TextWidget with a
+  string to datetime converter.  There is also a DateWidget that only
+  handles dates.
+
+Boolean Widgets
+~~~~~~~~~~~~~~~
+
+Boolean widgets' only responsibility is to convert some binary input
+to the Python values True or False.
+
+- CheckBoxWidget: This widget displays a single checkbox widget that
+  can be either checked or unchecked, representing the state of the
+  boolean value.
+
+- BooleanRadioWidget: Two radio buttons are used to represent the
+  true and false state of the boolean.  One can pass the textual
+  value for the two states in the constructor.  The default is "on"
+  and "off" (or their translation for languages other than English).
+
+- BooleanSelectWidget, BooleanDropdownWidget: Similar to the
+  BooleanRadioWidget, textual representations of the true and false
+  state are used to select the value.  See SelectWidget and
+  DropdownWidget, respectively, for more details.
+
+
+Single Selection Widgets
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Widgets that allow a single item to be selected from a list of values
+are usually views of a field, a vocabulary and the request, instead
+of just the field and request pair.  Therefore so called
+proxy-widgets are used to map from field-request to
+field-vocabulary-request pairs.  For example the ChoiceInputWidget,
+which takes a Choice field and a request object, is simply a function
+that looks up another widget that is registered for the Choice field,
+its vocabulary and the request.  Below is a list of all available
+widgets that require the latter three inputs.
+
+- SelectWidget: This widget provides a multiply-sized selection
+  element where the options are populated through the vocabulary
+  terms.  If the field is not required, a "no value" option will be
+  available as well.  The user will allowed to only select one value
+  though, since the Choice field is not a sequence-based field.
+
+- DropdownWidget: As a simple derivative of the SelectWdiget, it has
+  its size set to "1", which makes it a dropdown box.  Dropdown boxes
+  have the advantage that they always just show one value, which
+  makes some more user-friendly for single selections.
+
+- RadioWidget: This widget displays a radio button for each term in
+  the vocabulary.  Radio buttons have the advantage that they always
+  show all choices and are therefore well suitable for small
+  vocabularies.
+
+
+Multiple Selections Widgets
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This group of widgets is used to display input forms collection-based
+fields, such as List or Set.  Similar to the single selection
+widgets, two proxy- widgets are used to look up the correct widget.
+The first step is to map from field- request to field- value_type-
+request using a widget called CollectionInputWidget.  This allows us
+to use different widgets when the value type is an Int or Choice
+field for example.  Optionally, a second proxy-widget is used to
+convert the field- value_type- request pair to a field- vocabulary-
+request pair, as it is the case when the value type is a choice
+field.
+
+- MultiSelectWidget: Creates a select element with the multiple
+  attribute set to true.  This creates a multi-selection box.  This
+  is especially useful for vocabularies with many terms.  Note that
+  if your vocabulary supports a query interface, you can even filter
+  your selectable items using queries.
+
+- MultiCheckBoxWidget: Similar to the multi-selection widget, this
+  widget allows multi-value selections of a given list, but uses
+  checkboxes instead of a list.  This widget is more useful for
+  smaller vocabularies.
+
+- TupleSequenceWidget: This widget is used for all cases where the
+  value type is not a Choice field.  It used the widget of the value
+  type field to add new values to the tuple.  Other input elements
+  are used to remove items.
+
+- ListSequenceWidget: This widget is equivalent to the previous one,
+  except that it generates lists instead of tuples.
+
+
+Miscellaneous Widgets
+~~~~~~~~~~~~~~~~~~~~~
+
+- FileWidget: This widget displays a file input element and makes
+  sure the received data is a file.  This field is ideal for quickly
+  uploading byte streams as required for the Bytes field.
+
+- ObjectWidget: The ObjectWidget is the view for an object field.  It
+  uses the schema of the object to construct an input form.  The
+  object factory, which is passed in as a constructor argument, is
+  used to build the object from the input afterwards.
+
+Here is a simple interactive example demonstrating the rendering and
+conversion functionality of a widget::
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> from zope.schema import Int
+  >>> from zope.app.form.browser import IntWidget
+  >>> field = Int(__name__='number', title=u'Number', min=0, max=10)
+  >>> request = TestRequest(form={'field.number': u'9'})
+  >>> widget = IntWidget(field, request)
+  >>> widget.hasInput()
+  True
+  >>> widget.getInputValue()
+   9
+  >>> print widget().replace(' ', '\n  ')
+  <input
+     class="textType"
+     id="field.number"
+     name="field.number"
+     size="10"
+     type="text"
+     value="9"
+ 
+  />
+
+- Line 1 & 5: For views, including widgets, we always need a request
+  object.  The TestRequest class is the quick and easy way to create
+  a request without much hassle.  For each presentation type there
+  exists a TestRequest class.  The class takes a form argument, which
+  is a dictionary of values contained in the HTML form.  The widget
+  will later access this information.
+
+- Line 2: Import an integer field.
+
+- Line 3 & 6: Import the widget that displays and converts an integer
+  from the HTML form.  Initializing a widget only requires a field
+  and a request.
+
+- Line 4: Create an integer field with the constraint that the value
+  must lie between 0 and 10.  The __name__ argument must be passed
+  here, since the field has not been initialized inside an interface,
+  where the __name__ would be automatically assigned.
+
+- Line 7-8: This method checks whether the form contained a value for
+  this widget.
+
+- Line 9-10: If so, then we can use the getInputValue() method to
+  return the converted and validated value (an integer in this case).
+  If we would have chosen an integer outside this range, a
+  WidgetInputError would have been raised.
+
+- Line 11-20: Display the HTML representation of the widget.  The
+  replace() call is only for better readability of the output.
+
+Note that you usually will not have to deal with these methods at all
+manually, since the form generator and data converter does all the
+work for you.  The only method you will commonly overwrite is
+_validate(), which you will use to validate custom values.  This
+brings us right into the next subject, customizing widgets.
+
+There are two ways of customizing widgets.  For small adjustments to
+some parameters (properties of the widget), one can use the
+browser:widget subdirective of the browser:addform and
+browser:editform directives.  For example, to change the widget for a
+field called "name", the following ZCML code can be used.
+
+::
+
+  <browser:addform
+    ... >
+ 
+    <browser:widget
+        field="name"
+        class="zope.app.form.browser.TextWidget"
+        displayWidth="45"
+        style="width: 100%"/>
+ 
+  </browser:addform>
+
+In this case we force the system to use the TextWidget for the name,
+set the display width to 45 characters and add a style attribute that
+should try to set the width of the input box to the available width.
+
+The second possibility to change the widget of a field is to write a
+custom view class.  In there, custom widgets are easily realized
+using the CustomWidget wrapper class.  Here is a brief example::
+
+  from zope.app.form.widget import CustomWidget
+  from zope.app.form.browser import TextWidget
+
+  class CustomTextWidget(TextWidget):
+      ...
+
+  class SomeView:
+      name_widget = CustomWidget(CustomTextWidget)
+
+- Line 1: Since CustomWidget is presentation type independent, it is
+  defined in zope.app.form.widget.
+
+- Line 4-5: You simply extend an existing widget.  Here you can
+  overwrite everything, including the _validate() method.
+
+- Line 7-8: You can hook in the custom widget by adding an attribute
+  called name_widget, where name is the name of the field.  The value
+  of the attribute is a CustomWidget instance.  CustomWidget has only
+  one required constructor argument, which is the custom widget for
+  the field.  Other keyword arguments can be specified as well, which
+  will be set as attributes on the widget.
+
+More information about schemas can be found in the README.txt file of
+the zope.schema package.  The Zope 3 development Web site also
+contains some additional material.
+
+This concludes our introduction to schemas and forms.  For examples
+of schemas and forms in practice, see the first chapters of the
+"Content Components - The Basics" part.
+
+.. _http://zope.org/Members/infrae/Formulator:
+    http://zope.org/Members/infrae/Formulator
+.. _http://localhost:8080/++apidoc++: http://localhost:8080/++apidoc++



More information about the Checkins mailing list