[Zope3-checkins] SVN: Zope3/trunk/ Implemented browser:form
directive. Now you can use forms without having
Stephan Richter
srichter at cosmos.phy.tufts.edu
Sun Feb 27 07:18:55 EST 2005
Log message for revision 29321:
Implemented browser:form directive. Now you can use forms without having
to have an object that supports the schema edited.
See Zope3/src/zope/app/form/browser/form.txt
Changed:
U Zope3/trunk/doc/CHANGES.txt
U Zope3/trunk/doc/TODO.txt
U Zope3/trunk/src/zope/app/apidoc/bookmodule/book.zcml
U Zope3/trunk/src/zope/app/form/browser/configure.zcml
A Zope3/trunk/src/zope/app/form/browser/form.txt
A Zope3/trunk/src/zope/app/form/browser/formview.py
U Zope3/trunk/src/zope/app/form/browser/meta.zcml
U Zope3/trunk/src/zope/app/form/browser/metaconfigure.py
U Zope3/trunk/src/zope/app/form/browser/metadirectives.py
A Zope3/trunk/src/zope/app/form/browser/tests/test_form.py
-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/doc/CHANGES.txt 2005-02-27 12:18:55 UTC (rev 29321)
@@ -10,6 +10,12 @@
New features
+ - Developed a generic browser:form directive. It is pretty much the same
+ as the browser:editform directive, except that the data is not stored
+ on some context or adapted context but sent as a dictionary to special
+ method (by default). By default these methods are named `getData()` and
+ `setData(data)`.
+
- When raising the Unauthorized exception, the security checker
now passes the object in question in addition to the attribute
name and missing permission. This should make debugging easier.
Modified: Zope3/trunk/doc/TODO.txt
===================================================================
--- Zope3/trunk/doc/TODO.txt 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/doc/TODO.txt 2005-02-27 12:18:55 UTC (rev 29321)
@@ -9,8 +9,6 @@
- Fix final issues relating the new PAS and related UIs
-- Develop a generic browser:form directive
-
- Support for iterable sources
- Issue 292: Add factory to browser:resource directive
Modified: Zope3/trunk/src/zope/app/apidoc/bookmodule/book.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/bookmodule/book.zcml 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/apidoc/bookmodule/book.zcml 2005-02-27 12:18:55 UTC (rev 29321)
@@ -231,32 +231,6 @@
/>
</configure>
- <!-- Widgets and Forms -->
- <bookchapter
- id="form"
- title="Widgets and Forms"
- />
- <configure package="zope.app.form.browser">
- <bookchapter
- id="bwidget"
- title="Basic Widgets"
- doc_path="README.txt"
- parent="form"
- />
- <bookchapter
- id="swidget"
- title="Source Widgets"
- doc_path="source.txt"
- parent="form"
- />
- <bookchapter
- id="awidget"
- title="Advanced Widgets"
- doc_path="widgets.txt"
- parent="form"
- />
- </configure>
-
<!-- Workflow -->
<configure package="zope.wfmc">
<bookchapter
Modified: Zope3/trunk/src/zope/app/form/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/configure.zcml 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/configure.zcml 2005-02-27 12:18:55 UTC (rev 29321)
@@ -1,6 +1,6 @@
<configure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:browser="http://namespaces.zope.org/browser">
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser">
<!-- Form Macros -->
@@ -418,6 +418,41 @@
factory=".source.SourceListInputWidget"
permission="zope.Public"
/>
-
+ <!-- Register the form documentation with the apidoc tool -->
+ <configure
+ xmlns:apidoc="http://namespaces.zope.org/apidoc"
+ xmlns:zcml="http://namespaces.zope.org/zcml"
+ zcml:condition="have apidoc">
+
+ <apidoc:bookchapter
+ id="form"
+ title="Widgets and Forms"
+ />
+ <apidoc:bookchapter
+ id="bwidget"
+ title="Basic Widgets"
+ doc_path="README.txt"
+ parent="form"
+ />
+ <apidoc:bookchapter
+ id="swidget"
+ title="Source Widgets"
+ doc_path="source.txt"
+ parent="form"
+ />
+ <apidoc:bookchapter
+ id="awidget"
+ title="Advanced Widgets"
+ doc_path="widgets.txt"
+ parent="form"
+ />
+ <apidoc:bookchapter
+ id="formdirective"
+ title="The browser:form Directive"
+ doc_path="form.txt"
+ parent="form"
+ />
+ </configure>
+
</configure>
Added: Zope3/trunk/src/zope/app/form/browser/form.txt
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/form.txt 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/form.txt 2005-02-27 12:18:55 UTC (rev 29321)
@@ -0,0 +1,207 @@
+=============
+Generic Forms
+=============
+
+The `browser:form` allows the developer to use the form and widget framework
+without relying on a particular context object. Instead, it is up to the
+developer to implement two simple methods that handle the retrieval and
+mutation of the data in form of a dictionary.
+
+But I am getting ahead of myself. We first need to define a schema that we
+would like to use for our form:
+
+ >>> import zope.interface
+ >>> import zope.schema
+ >>> class IName(zope.interface.Interface):
+ ... """The name of a person."""
+ ...
+ ... first = zope.schema.TextLine(
+ ... title=u"First Name",
+ ... required=False)
+ ...
+ ... last = zope.schema.TextLine(
+ ... title=u"Last Name",
+ ... required=True)
+
+Now we are almost ready to create the form view. But first we have to create a
+class that knows how to get and set values for the fields described by the
+schema. The system calls for a class (that will be used as a mixin) that
+implements a method called `getData()` that returns a dictionary mapping field
+names to values and a second method called `setData(data)` that takes a data
+dictionary as argument and handles the data in any way fit. In our case, let's
+store the data in a global attribute called name:
+
+ >>> name = ['John', 'Doe']
+
+ >>> class DataHandler(object):
+ ...
+ ... def getData(self):
+ ... global name
+ ... return {'first': name[0], 'last': name[1]}
+ ...
+ ... def setData(self, data):
+ ... global name
+ ... name[0] = data['first']
+ ... name[1] = data['last']
+
+
+We now immitate the form-directive's behavior and create the view class:
+
+ >>> from zope.app.form.browser.formview import FormView
+ >>> View = type('View', bases=(DataHandler, FormView),
+ ... dict={'schema': IName})
+
+To initialize the view, you still need a context and a request. The context is
+useful because it gives you a location, so that you can look up local
+components to store the data, such as the principal annotations. In our case
+we do not care about the context, so it will be just `None`:
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+ >>> view = View(None, request)
+
+We can now look at the widgets and see that the data was correctly loaded:
+
+ >>> view.first_widget()
+ u'<input class="textType" id="field.first" name="field.first"
+ size="20" type="text" value="John" />'
+
+ >>> view.last_widget()
+ u'<input class="textType" id="field.last" name="field.last"
+ size="20" type="text" value="Doe" />'
+
+Now when a request is sent in, the data is transmitted in the form
+
+ >>> request.form['field.first'] = u'Stephan'
+ >>> request.form['field.last'] = u'Richter'
+ >>> request.form['UPDATE_SUBMIT'] = u''
+
+and as soon as the `update()` method is called
+
+ >>> view.update()
+ ''
+
+the global name variable is changed:
+
+ >>> name
+ [u'Stephan', u'Richter']
+
+And that's pretty much all that there is to it. The rest behaves exactely like
+an edit form, from which the form view was derived.
+
+Of course you can also completely overwrite the `update()` method like for the
+edit view removing the requirement to implement the `getData()` and
+`setData()` methods.
+
+
+Using the `browser:form` directive
+----------------------------------
+
+Let's now see how the form directive works. The first task is to load the
+necessary meta-configuration:
+
+ >>> from zope.configuration import xmlconfig
+
+ >>> import zope.app.component
+ >>> context = xmlconfig.file('meta.zcml', zope.app.component)
+
+ >>> import zope.app.form.browser
+ >>> context = xmlconfig.file('meta.zcml', zope.app.form.browser, context)
+
+ >>> import zope.app.publisher.browser
+ >>> context = xmlconfig.file('meta.zcml', zope.app.publisher.browser,
+ ... context)
+
+Before we run the directive, make sure that no view exists:
+
+ >>> class IAnything(zope.interface.Interface):
+ ... pass
+
+ >>> class Anything(object):
+ ... zope.interface.implements(IAnything)
+
+ >>> from zope.publisher.browser import TestRequest
+
+ >>> from zope.app import zapi
+ >>> zapi.queryMultiAdapter((Anything(), TestRequest()), name='name.html')
+
+Now that we know that the view did not exist before the registration, let's
+execute the form directive:
+
+ >>> import sys
+ >>> sys.modules['form'] = type(
+ ... 'Module', (),
+ ... {'DataHandler': DataHandler,
+ ... 'IAnything': IAnything,
+ ... 'IName': IName})()
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:browser="http://namespaces.zope.org/browser"
+ ... i18n_domain="zope">
+ ...
+ ... <view
+ ... type="zope.publisher.interfaces.browser.IBrowserRequest"
+ ... for="zope.schema.interfaces.ITextLine"
+ ... provides="zope.app.form.interfaces.IInputWidget"
+ ... factory="zope.app.form.browser.TextWidget"
+ ... permission="zope.Public"
+ ... />
+ ...
+ ... <browser:form
+ ... for="form.IAnything"
+ ... schema="form.IName"
+ ... class="form.DataHandler"
+ ... name="name.html"
+ ... label="Edit the name"
+ ... fields="first last"
+ ... permission="zope.Public" />
+ ...
+ ... </configure>
+ ... ''', context)
+
+and try to look up the view again:
+
+ >>> zapi.queryMultiAdapter(
+ ... (Anything(), TestRequest()), name='name.html') #doctest:+ELLIPSIS
+ <zope.app.pagetemplate.simpleviewclass.SimpleViewClass from edit.pt ...>
+
+
+Now, if I do not specify my own template, and my class does not overwrite the
+`update()` method, then the class *must* implement `getData()` and
+`setData(data)`, otherwise a configuration error is raised:
+
+ >>> class NewDataHandler(object):
+ ...
+ ... def getData(self):
+ ... return {}
+
+ >>> sys.modules['form'].NewDataHandler = NewDataHandler
+
+ >>> context = xmlconfig.string('''
+ ... <configure
+ ... xmlns:browser="http://namespaces.zope.org/browser"
+ ... i18n_domain="zope">
+ ...
+ ... <browser:form
+ ... for="form.IAnything"
+ ... schema="form.IName"
+ ... class="form.NewDataHandler"
+ ... name="name.html"
+ ... label="Edit the name"
+ ... fields="first last"
+ ... permission="zope.Public" />
+ ...
+ ... </configure>
+ ... ''', context)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 6.6
+ ConfigurationError: You must specify a class that implements
+ `getData()` and `setData()`, if you do not
+ overwrite `update()`.
+
+Now we need to clean up afterwards.
+
+ >>> del sys.modules['form']
\ No newline at end of file
Property changes on: Zope3/trunk/src/zope/app/form/browser/form.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: Zope3/trunk/src/zope/app/form/browser/formview.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/formview.py 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/formview.py 2005-02-27 12:18:55 UTC (rev 29321)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Form View Classes
+
+$Id: editview.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from transaction import get_transaction
+
+from zope.app.form.interfaces import WidgetsError
+
+from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
+from zope.app.form.browser.editview import EditView
+from zope.app.form.browser.submit import Update
+from zope.app.i18n import ZopeMessageIDFactory as _
+
+
+class Data(dict):
+ """Dictionary wrapper to make keys available as attributes."""
+
+ def __getattr__(self, name):
+ return self[name]
+
+ def __setattr__(self, name, value):
+ self[name] = value
+
+
+class FormView(EditView):
+
+ def getData(self):
+ """Get the data for the form.
+
+ This method should return a dictionary mapping field names to values.
+ """
+ NotImplemented, 'Must be implemented by a specific form class'
+
+ def setData(self, data):
+ """Set the data gotten from a form.
+
+ The data will be a dictionary of fieldnames to values.
+ """
+ NotImplemented, 'Must be implemented by a specific form class'
+
+ def _setUpWidgets(self):
+ self.data = Data(self.getData())
+ setUpEditWidgets(
+ self, self.schema, source=self.data, names=self.fieldNames)
+
+ def update(self):
+ if self.update_status is not None:
+ # We've been called before. Just return the status we previously
+ # computed.
+ return self.update_status
+
+ status = ''
+
+ if Update in self.request:
+ try:
+ changed = applyWidgetsChanges(
+ self, self.schema, target=self.data, names=self.fieldNames)
+ except WidgetsError, errors:
+ self.errors = errors
+ status = _("An error occured.")
+ get_transaction().abort()
+ else:
+ if changed:
+ self.setData(self.data)
+ setUpEditWidgets(
+ self, self.schema, source=self.data,
+ ignoreStickyValues=True, names=self.fieldNames)
+
+ self.update_status = status
+ return status
Property changes on: Zope3/trunk/src/zope/app/form/browser/formview.py
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/app/form/browser/meta.zcml
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/meta.zcml 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/meta.zcml 2005-02-27 12:18:55 UTC (rev 29321)
@@ -34,6 +34,20 @@
<meta:complexDirective
+ name="form"
+ schema=".metadirectives.IFormDirective"
+ handler=".metaconfigure.FormDirective"
+ >
+
+ <meta:subdirective
+ name="widget"
+ schema=".metadirectives.IWidgetSubdirective"
+ />
+
+ </meta:complexDirective>
+
+
+ <meta:complexDirective
name="editform"
schema=".metadirectives.IEditFormDirective"
handler=".metaconfigure.EditFormDirective"
Modified: Zope3/trunk/src/zope/app/form/browser/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/metaconfigure.py 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/metaconfigure.py 2005-02-27 12:18:55 UTC (rev 29321)
@@ -34,6 +34,7 @@
from zope.app.form.interfaces import IInputWidget, IDisplayWidget
from add import AddView, AddViewFactory
from editview import EditView, EditViewFactory
+from formview import FormView
from addwizard import AddWizardView, AddWizardViewFactory
from editwizard import EditWizardView, EditWizardViewFactory
from schemadisplay import DisplayView, DisplayViewFactory
@@ -299,6 +300,20 @@
kw={'menu': self.menu},
)
+class FormDirective(EditFormDirective):
+
+ view = FormView
+
+ def __init__(self, _context, **kwargs):
+ super(FormDirective, self).__init__(_context, **kwargs)
+ attrs = self.class_.__dict__.keys()
+ if 'template' not in kwargs.keys() and 'update' not in attrs and \
+ ('getData' not in attrs or 'setData' not in attrs):
+ raise ConfigurationError(
+ "You must specify a class that implements `getData()` "
+ "and `setData()`, if you do not overwrite `update()`.")
+
+
class SubeditFormDirective(EditFormDirectiveBase):
default_template = 'subedit.pt'
Modified: Zope3/trunk/src/zope/app/form/browser/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/metadirectives.py 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/metadirectives.py 2005-02-27 12:18:55 UTC (rev 29321)
@@ -262,6 +262,24 @@
value_type=PythonIdentifier()
)
+class IFormDirective(ICommonFormInformation):
+ """
+ Define an automatically generated form.
+
+ The form directive does nto require the data to be stored in its context,
+ but leaves the storing procedure to the to a method.
+ """
+ class_ = GlobalObject(
+ title=u"Class",
+ description=u"""
+ A class to provide the `getData()` and `setData()` methods or
+ completely custom methods to be used by a custom template.
+
+ This class is used as a mix-in class. As a result, it needn't
+ subclass any special classes, such as BrowserView.""",
+ required=True
+ )
+
class IEditFormDirective(ICommonFormInformation):
"""
Define an automatically generated edit form
Added: Zope3/trunk/src/zope/app/form/browser/tests/test_form.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_form.py 2005-02-27 12:17:03 UTC (rev 29320)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_form.py 2005-02-27 12:18:55 UTC (rev 29321)
@@ -0,0 +1,41 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for the ZCML Documentation Module
+
+$Id: tests.py 29269 2005-02-23 22:22:48Z srichter $
+"""
+import os
+import unittest
+from zope.testing import doctest, doctestunit
+from zope.app.testing import placelesssetup, ztapi
+
+from zope.schema.interfaces import ITextLine
+from zope.app.form.browser import TextWidget
+from zope.app.form.interfaces import IInputWidget
+
+def setUp(test):
+ placelesssetup.setUp()
+ ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
+
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('../form.txt',
+ setUp=setUp, tearDown=placelesssetup.tearDown,
+ globs={'pprint': doctestunit.pprint},
+ optionflags=doctest.NORMALIZE_WHITESPACE),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(default='test_suite')
Property changes on: Zope3/trunk/src/zope/app/form/browser/tests/test_form.py
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Zope3-Checkins
mailing list