[Checkins] SVN: z3c.form/branches/fieldsandcontentproviders/ merged trunk from r108949 until r109920
Godefroid Chapelle
gotcha at bubblenet.be
Fri Mar 12 06:06:02 EST 2010
Log message for revision 109921:
merged trunk from r108949 until r109920
Changed:
_U z3c.form/branches/fieldsandcontentproviders/
U z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt
U z3c.form/branches/fieldsandcontentproviders/CHANGES.txt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml
A z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml
A z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py
U z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt
-=-
Property changes on: z3c.form/branches/fieldsandcontentproviders
___________________________________________________________________
Modified: svn:ignore
- coverage
develop-eggs
eggs
parts
.installed.cfg
build
dist
bin
docs
+ coverage
develop-eggs
eggs
parts
.installed.cfg
build
dist
bin
docs
.project
.pydevproject
Modified: z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/AUTHOR.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -18,4 +18,5 @@
Martijn Faassen
Michael Howitz
Michael Kerrin
-Paul Carduner
\ No newline at end of file
+Paul Carduner
+Martin Aspeli
\ No newline at end of file
Modified: z3c.form/branches/fieldsandcontentproviders/CHANGES.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/CHANGES.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/CHANGES.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -5,9 +5,32 @@
2.3.3 (unreleased)
------------------
+- The last discriminator of the 'message' IValue adapter used in the
+ ErrorViewSnippet is called 'content', but it was looked up as the error view
+ itself. It is now looked up on the form's context.
+
+- Don't let util.getSpecification() generate an interface more than once.
+ This causes strange effects when used in value adapters: if two adapters
+ use e.g. ISchema['some_field'] as a "discriminator" for 'field', with one
+ adapter being more specific on a discriminator that comes later in the
+ discriminator list (e.g. 'form' for an ErrorViewMessage), then depending on
+ the order in which these two were set up, the adapter specialisation may
+ differ, giving unexpected results that make it look like the adapter
+ registry is picking the wrong adapter.
+
+- Fix trivial test failures on Python 2.4 stemming from differences in
+ pprint's sorting of dicts.
+
+- Don't invoke render() when publishing the form as a view if the HTTP status
+ code has been set to one in the 3xx range (e.g. a redirect or not-modified
+ response) - the response body will be ignored by the browser anyway.
+
+- Handle Invalid exceptions from constraints and field validators.
+
- Don't create unnecessary self.items in update() method of
SelectWidget in DISPLAY_MODE. Now items is a property.
+- Add hidden widget templates for radio buttons and checkboxes.
2.3.2 (2010-01-21)
------------------
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -110,7 +110,27 @@
</span>
<input name="widget.name-empty-marker" type="hidden" value="1" />
+Check HIDDEN_MODE:
+ >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+ ... 'checkbox_hidden.pt')
+ >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+ >>> zope.component.provideAdapter(factory,
+ ... (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+ ... IPageTemplate, name='hidden')
+
+ >>> widget.value = 'true'
+ >>> widget.mode = interfaces.HIDDEN_MODE
+ >>> print widget.render()
+ <span class="option">
+ <input type="hidden" id="widget-id-0" name="widget.name:list"
+ class="checkbox-widget" value="true" />
+ </span><span class="option">
+ <input type="hidden" id="widget-id-1" name="widget.name:list"
+ class="checkbox-widget" value="false" />
+ </span>
+
+
Single Checkbox Widget
----------------------
@@ -198,3 +218,21 @@
<input name="widget.name-empty-marker" type="hidden"
value="1" />
+Check HIDDEN_MODE:
+
+ >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+ ... 'checkbox_hidden.pt')
+ >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+ >>> zope.component.provideAdapter(factory,
+ ... (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+ ... IPageTemplate, name='hidden')
+
+ >>> widget.value = 'true'
+ >>> widget.mode = interfaces.HIDDEN_MODE
+ >>> print widget.render()
+ <span class="option">
+ <input type="hidden" id="widget-id-0"
+ name="widget.name:list"
+ class="single-checkbox-widget" value="selected" />
+ </span>
+
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox.zcml 2010-03-12 11:06:00 UTC (rev 109921)
@@ -35,4 +35,11 @@
template="checkbox_input.pt"
/>
+ <z3c:widgetTemplate
+ mode="hidden"
+ widget="z3c.form.interfaces.ICheckBoxWidget"
+ layer="z3c.form.interfaces.IFormLayer"
+ template="checkbox_hidden.pt"
+ />
+
</configure>
Copied: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt (from rev 109920, z3c.form/trunk/src/z3c/form/browser/checkbox_hidden.pt)
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt (rev 0)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/checkbox_hidden.pt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ tal:omit-tag="">
+<span class="option"
+ tal:repeat="item view/items">
+ <input id="" name="" value="" class="hidden-widget" title=""
+ tabindex="" accesskey=""
+ type="hidden"
+ tal:attributes="id item/id;
+ name item/name;
+ class view/klass;
+ value item/value;
+ style view/style;
+ title view/title;
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup;
+ disabled view/disabled;
+ tabindex view/tabindex;
+ onfocus view/onfocus;
+ onblur view/onblur;
+ onchange view/onchange;
+ readonly view/readonly;
+ alt view/alt;
+ accesskey view/accesskey;
+ onselect view/onselect" />
+</span>
+</html>
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -105,3 +105,18 @@
</label>
</span>
<input name="widget.name-empty-marker" type="hidden" value="1" />
+
+Check HIDDEN_MODE:
+
+ >>> template = os.path.join(os.path.dirname(z3c.form.browser.__file__),
+ ... 'radio_hidden.pt')
+ >>> factory = z3c.form.widget.WidgetTemplateFactory(template)
+ >>> zope.component.provideAdapter(factory,
+ ... (zope.interface.Interface, IDefaultBrowserLayer, None, None, None),
+ ... IPageTemplate, name='hidden')
+
+ >>> widget.value = 'true'
+ >>> widget.mode = interfaces.HIDDEN_MODE
+ >>> print widget.render()
+ <input id="widget-id" name="widget.name" value="true" class="hidden-widget" type="hidden" />
+
\ No newline at end of file
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio.zcml 2010-03-12 11:06:00 UTC (rev 109921)
@@ -30,4 +30,11 @@
template="radio_input.pt"
/>
+ <z3c:widgetTemplate
+ mode="hidden"
+ widget="z3c.form.interfaces.IRadioWidget"
+ layer="z3c.form.interfaces.IFormLayer"
+ template="radio_hidden.pt"
+ />
+
</configure>
Copied: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt (from rev 109920, z3c.form/trunk/src/z3c/form/browser/radio_hidden.pt)
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt (rev 0)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/browser/radio_hidden.pt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -0,0 +1,13 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ tal:omit-tag="">
+<input id="" name="" value="" class="hidden-widget" title=""
+ tabindex="" accesskey=""
+ type="hidden"
+ tal:attributes="id view/id;
+ name view/name;
+ title view/title;
+ tabindex view/tabindex;
+ accesskey view/accesskey;
+ value view/value" />
+</div>
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/error.py 2010-03-12 11:06:00 UTC (rev 109921)
@@ -68,7 +68,7 @@
def update(self):
value = zope.component.queryMultiAdapter(
(self.context, self.request, self.widget,
- self.field, self.form, self),
+ self.field, self.form, self.content),
interfaces.IValue, name='message')
if value is not None:
self.message = value.get()
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.py 2010-03-12 11:06:00 UTC (rev 109921)
@@ -280,7 +280,7 @@
self.hasRequiredFields = True
uniqueOrderedKeys.append(shortName)
if newWidget:
- # self._data_values.append(widget)
+ self._data_values.append(widget)
self._data[shortName] = widget
zope.location.locate(widget, self, shortName)
# allways ensure that we add all keys and keep the order given from
@@ -307,7 +307,7 @@
getattr(widget, 'field', None),
widget),
interfaces.IValidator).validate(value)
- except (zope.schema.ValidationError,
+ except (zope.interface.Invalid,
ValueError, MultipleErrors), error:
view = zope.component.getMultiAdapter(
(error, self.request, widget, widget.field,
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/field.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -330,7 +330,12 @@
>>> class LastNameTooShort(zope.schema.interfaces.ValidationError):
... """The last name is too short."""
-
+
+ >>> def lastNameConstraint(value):
+ ... if value and value == value.lower():
+ ... raise zope.interface.Invalid(u"Name must have at least one capital letter")
+ ... return True
+
>>> class IPerson(zope.interface.Interface):
... id = zope.schema.TextLine(
... title=u'ID',
@@ -342,7 +347,8 @@
... title=u'Last Name',
... description=u"The person's last name.",
... default=u'',
- ... required=True)
+ ... required=True,
+ ... constraint=lastNameConstraint)
...
... firstName = zope.schema.TextLine(
... title=u'First Name',
@@ -698,15 +704,20 @@
>>> manager.ignoreContext = True
>>> manager.update()
- >>> from pprint import pprint
- >>> pprint(manager.extract())
- ({'firstName': u'Stephan', 'lastName': u'Richter'}, ())
+ >>> data, errors = manager.extract()
+ >>> data['firstName']
+ u'Stephan'
+ >>> data['lastName']
+ u'Richter'
+ >>> errors
+ ()
Since all errors are immediately converted to error view snippets, we have to
provide the adapter from a validation error to an error view snippet first:
>>> from z3c.form import error
>>> zope.component.provideAdapter(error.ErrorViewSnippet)
+ >>> zope.component.provideAdapter(error.InvalidErrorViewSnippet)
Let's now cause a widget-level error by not submitting the required last
name:
@@ -719,6 +730,24 @@
>>> manager.extract()
({'firstName': u'Stephan'}, (<ErrorViewSnippet for RequiredMissing>,))
+Or, we could violate a constraint. This constraint raises Invalid, which is
+a convenient way to raise errors where we mainly care about providing a custom
+error message.
+
+ >>> request = TestRequest(form={
+ ... 'form.widgets.firstName': u'Stephan',
+ ... 'form.widgets.lastName': u'richter',
+ ... 'form.widgets.id': u'srichter'})
+ >>> manager = field.FieldWidgets(personForm, request, context)
+ >>> manager.ignoreContext = True
+ >>> manager.update()
+ >>> extracted = manager.extract()
+ >>> extracted
+ ({'firstName': u'Stephan'}, (<InvalidErrorViewSnippet for Invalid>,))
+
+ >>> extracted[1][0].createMessage()
+ u'Name must have at least one capital letter'
+
Finally, let's ensure that invariant failures are also caught:
>>> request = TestRequest(form={
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.py 2010-03-12 11:06:00 UTC (rev 109921)
@@ -211,6 +211,11 @@
def __call__(self):
self.update()
+
+ # Don't render anything if we are doing a redirect
+ if self.request.response.getStatus() in (300, 301, 302, 303, 304, 305, 307,):
+ return u''
+
return self.render()
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/form.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -341,7 +341,62 @@
</body>
</html>
+The update()/render() cycle is what happens when the form is called, i.e.
+when it is published:
+ >>> print addForm()
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+ <form action=".">
+ <div class="row">
+ <label for="form-widgets-id">ID</label>
+ <input type="text" id="form-widgets-id"
+ name="form.widgets.id"
+ class="text-widget required textline-field"
+ value="" />
+ </div>
+ <div class="row">
+ <label for="form-widgets-name">Name</label>
+ <input type="text" id="form-widgets-name" name="form.widgets.name"
+ class="text-widget required textline-field"
+ value="" />
+ </div>
+ <div class="row">
+ <label for="form-widgets-gender">Gender</label>
+ <select id="form-widgets-gender" name="form.widgets.gender:list"
+ class="select-widget choice-field" size="1">
+ <option id="form-widgets-gender-novalue"
+ value="--NOVALUE--">no value</option>
+ <option id="form-widgets-gender-0" value="male">male</option>
+ <option id="form-widgets-gender-1" value="female">female</option>
+ </select>
+ <input name="form.widgets.gender-empty-marker" type="hidden"
+ value="1" />
+ </div>
+ <div class="row">
+ <label for="form-widgets-age">Age</label>
+ <input type="text" id="form-widgets-age" name="form.widgets.age"
+ class="text-widget int-field" value="20" />
+ </div>
+ <div class="action">
+ <input type="submit" id="form-buttons-add" name="form.buttons.add"
+ class="submit-widget button-field" value="Add" />
+ </div>
+ </form>
+ </body>
+ </html>
+
+Note that we don't actually call render if the response has been set to a 3xx
+type status code (e.g. a redirect or not modified response), since the browser
+would not render it anyway:
+
+ >>> request.response.setStatus(304)
+ >>> print addForm()
+
+Let's go back to a normal status to continue the test.
+
+ >>> request.response.setStatus(200)
+
Submitting an add form successfully
-----------------------------------
@@ -1064,12 +1119,16 @@
>>> editForm = PersonDictEditForm(None, request)
>>> editForm.update()
- >>> from pprint import pprint
- >>> pprint(personDict)
- {'age': 5,
- 'gender': 'male',
- 'id': u'rineichen',
- 'name': u'Jesse Ineichen'}
+ >>> len(personDict)
+ 4
+ >>> personDict['age']
+ 5
+ >>> personDict['gender']
+ 'male'
+ >>> personDict['id']
+ u'rineichen'
+ >>> personDict['name']
+ u'Jesse Ineichen'
Creating a Display Form
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.py 2010-03-12 11:06:00 UTC (rev 109921)
@@ -56,13 +56,24 @@
(spec is not None and
not zope.interface.interfaces.ISpecification.providedBy(spec)
and not isinstance(spec, classTypes)) ):
- # Step 1: Create an interface
- iface = zope.interface.interface.InterfaceClass(
- 'IGeneratedForObject_%i' %hash(spec))
- # Step 2: Directly-provide the interface on the specification
- zope.interface.alsoProvides(spec, iface)
- # Step 3: Make the new interface the specification for this instance
- spec = iface
+
+ # Step 1: Calculate an interface name
+ ifaceName = 'IGeneratedForObject_%i' %hash(spec)
+
+ # Step 2: Find out if we already have such an interface
+ existingInterfaces = [
+ i for i in zope.interface.directlyProvidedBy(spec)
+ if i.__name__ == ifaceName
+ ]
+
+ # Step 3a: Return an existing interface if there is one
+ if len(existingInterfaces) > 0:
+ spec = existingInterfaces[0]
+ # Step 3b: Create a new interface if not
+ else:
+ iface = zope.interface.interface.InterfaceClass(ifaceName)
+ zope.interface.alsoProvides(spec, iface)
+ spec = iface
return spec
Modified: z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt
===================================================================
--- z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt 2010-03-12 03:09:23 UTC (rev 109920)
+++ z3c.form/branches/fieldsandcontentproviders/src/z3c/form/util.txt 2010-03-12 11:06:00 UTC (rev 109921)
@@ -396,3 +396,50 @@
True
That's all.
+
+`getSpecification()` function
+-----------------------------
+
+This function is capable of returning an `ISpecification` for any object,
+including instances.
+
+For an interface, it simply returns the interface:
+
+ >>> import zope.interface
+ >>> class IFoo(zope.interface.Interface):
+ ... pass
+
+ >>> util.getSpecification(IFoo) == IFoo
+ True
+
+Ditto for a class:
+
+ >>> class Bar(object):
+ ... pass
+
+ >>> util.getSpecification(Bar) == Bar
+ True
+
+For an instance, it will create a marker interface on the fly if necessary:
+
+ >>> bar = Bar()
+ >>> util.getSpecification(bar) # doctest: +ELLIPSIS
+ <InterfaceClass z3c.form.util.IGeneratedForObject_...>
+
+The ellipsis represents a hash of the object.
+
+If the function is called twice on the same object, it will not create a new
+marker each time:
+
+ >>> baz = Bar()
+ >>> barMarker = util.getSpecification(bar)
+ >>> bazMarker1 = util.getSpecification(baz)
+ >>> bazMarker2 = util.getSpecification(baz)
+
+ >>> barMarker is bazMarker1
+ False
+
+ >>> bazMarker1 == bazMarker2
+ True
+ >>> bazMarker1 is bazMarker2
+ True
More information about the checkins
mailing list