A widget for z3c.relationfield.
Project description
This package implements a zope.formlib compatible widget for relations as defined by z3c.relationfield.
This package does not provide a z3c.form widget for z3c.relationfield, but it is hoped that will eventually be developed as well (in another package).
Setup
In order to demonstrate our widget, we need to first set up a relation field (for the details of this see z3c.relationfield’s documentation):
>>> from z3c.relationfield import Relation >>> from zope.interface import Interface >>> class IItem(Interface): ... rel = Relation(title=u"Relation") >>> from z3c.relationfield.interfaces import IHasRelations >>> from persistent import Persistent >>> from zope.interface import implements >>> class Item(Persistent): ... implements(IItem, IHasRelations) ... def __init__(self): ... self.rel = None >>> from zope.app.component.site import SiteManagerContainer >>> from zope.app.container.btree import BTreeContainer >>> class TestApp(SiteManagerContainer, BTreeContainer): ... pass
Set up the application with the right utilities:
>>> root = getRootFolder()['root'] = TestApp() >>> from zope.app.component.site import LocalSiteManager >>> root.setSiteManager(LocalSiteManager(root)) >>> from zope.app.component.hooks import setSite >>> setSite(root) >>> from zope.app.intid import IntIds >>> from zope.app.intid.interfaces import IIntIds >>> root['intids'] = intids = IntIds() >>> sm = root.getSiteManager() >>> sm.registerUtility(intids, provided=IIntIds) >>> from z3c.relationfield import RelationCatalog >>> from zc.relation.interfaces import ICatalog >>> root['catalog'] = catalog = RelationCatalog() >>> sm.registerUtility(catalog, provided=ICatalog)
Items a and b with a relation from b to a:
>>> root['a'] = Item() >>> from z3c.relationfield import RelationValue >>> b = Item() >>> from zope import component >>> from zope.app.intid.interfaces import IIntIds >>> intids = component.getUtility(IIntIds) >>> a_id = intids.getId(root['a']) >>> b.rel = RelationValue(a_id) >>> root['b'] = b
We also need to set up a utility that knows how to generate an object path for a given object, and back:
>>> import grokcore.component as grok >>> from z3c.objpath.interfaces import IObjectPath >>> class ObjectPath(grok.GlobalUtility): ... grok.provides(IObjectPath) ... def path(self, obj): ... return obj.__name__ ... def resolve(self, path): ... try: ... return root[path] ... except KeyError: ... raise ValueError("Cannot resolve: %s" % path) >>> grok.testing.grok_component('ObjectPath', ObjectPath) True
Let’s also set up a broken relation:
>>> d = root['d'] = Item() >>> d_id = intids.getId(root['d']) >>> c = Item() >>> c.rel = RelationValue(d_id) >>> root['c'] = c >>> del root['d'] >>> root['c'].rel.to_object is None True >>> root['c'].rel.isBroken() True
The relation widget
The relation widget can be looked up for a relation field. The widget will render with a button that can be used to set the relation. Pressing this button will show a pop up window. The URL implementing the popup window is defined on a special view that needs to be available on the context object (that the relation is defined on). This view must be named “explorerurl”. We’ll provide one here:
>>> from zope.interface import Interface >>> import grokcore.view >>> class ExplorerUrl(grokcore.view.View): ... grok.context(Interface) ... def render(self): ... return 'http://grok.zope.org'
Now we can Grok the view:
>>> grok.testing.grok_component('ExplorerUrl', ExplorerUrl) True
Let’s take a look at the relation widget now:
>>> from zope.publisher.browser import TestRequest >>> from z3c.relationfieldui import RelationWidget >>> request = TestRequest() >>> field = IItem['rel'] >>> bound = field.bind(root['b']) >>> widget = RelationWidget(bound, request) >>> widget.setRenderedValue(bound.get(root['b'])) >>> print widget() <input class="textType" id="field.rel" name="field.rel" size="20" type="text" value="a" /><input class="buttonType" onclick="Z3C.relation.popup(this.previousSibling, 'http://grok.zope.org?from_attribute=rel&from_path=b')" type="button" value="get relation" />
Let’s also try it with the broken relation:
>>> bound = field.bind(root['c']) >>> widget = RelationWidget(bound, request) >>> widget.setRenderedValue(bound.get(root['c']))
When we render the widget, the value is still correct (even though it’s broken):
>>> print widget() <input class="textType" id="field.rel" name="field.rel" size="20" type="text" value="d" /><input class="buttonType" onclick="Z3C.relation.popup(this.previousSibling, 'http://grok.zope.org?from_attribute=rel&from_path=c')" type="button" value="get relation" />
Relation Choice
Let’s examine the RelationChoice field from z3c.relationfield. We need to provide a source of possible relations for it, and we can do this using the RelationSourceFactory:
>>> from z3c.relationfieldui import RelationSourceFactory >>> class MyRelationSourceFactory(RelationSourceFactory): ... def getTargets(self): ... return [root['a'], root['b'], root['c']]
In the source, we simply return an iterable of objects that are possible relation targets.
Let’s now create an object that makes use of this source:
>>> from z3c.relationfield import RelationChoice >>> class IItemChoice(Interface): ... rel = RelationChoice(title=u"Relation", required=False, ... source=MyRelationSourceFactory())
We can now take a look at the widget, using ChoiceInputWidget:
>>> from zope.app.form.browser import ChoiceInputWidget >>> class ItemChoice(Persistent): ... implements(IItemChoice, IHasRelations) ... def __init__(self): ... self.rel = None >>> root['choice_a'] = ItemChoice() >>> field = IItemChoice['rel'] >>> bound = field.bind(root['choice_a']) >>> widget = ChoiceInputWidget(bound, request)
Let’s first render the widget without a particular rendered value set:
>>> print widget() <div> <div class="value"> <select id="field.rel" name="field.rel" size="1" > <option selected="selected" value="">(no value)</option> <option value="a">a</option> <option value="b">b</option> <option value="c">c</option> </select> </div> <input name="field.rel-empty-marker" type="hidden" value="1" /> </div>
Let’s try it again with a value set as a relation to a:
>>> choice_b = ItemChoice() >>> choice_b.rel = RelationValue(a_id) >>> root['choice_b'] = choice_b >>> bound = field.bind(root['choice_b']) >>> widget = ChoiceInputWidget(bound, request) >>> widget.setRenderedValue(bound.get(root['b']))
When we look at the widget we see that this relation is indeed selected:
>>> print widget() <div> <div class="value"> <select id="field.rel" name="field.rel" size="1" > <option value="">(no value)</option> <option selected="selected" value="a">a</option> <option value="b">b</option> <option value="c">c</option> </select> </div> <input name="field.rel-empty-marker" type="hidden" value="1" /> </div>
Relation display widget
The display widget for relation will render a URL to the object it relates to. What this URL will be exactly can be controlled by defining a view on the object called “relationurl”. Without such a view, the display widget will link directly to the object:
>>> from z3c.relationfieldui import RelationDisplayWidget >>> bound = field.bind(root['b']) >>> widget = RelationDisplayWidget(bound, request) >>> widget.setRenderedValue(bound.get(root['b']))
The widget will point to the plain URL of rel’s to_object:
>>> print widget() <a href="http://127.0.0.1/root/a">a</a>
Now we register a special relationurl view:
>>> class RelationUrl(grokcore.view.View): ... grok.context(Interface) ... def render(self): ... return self.url('edit') >>> grok.testing.grok_component('RelationUrl', RelationUrl) True
We should now see a link postfixed with /edit:
>>> print widget() <a href="http://127.0.0.1/root/a/edit">a</a>
When the relation is broken, it will still display, but as broken:
>>> bound = field.bind(root['c']) >>> widget = RelationDisplayWidget(bound, request) >>> widget.setRenderedValue(bound.get(root['c'])) >>> print widget() Broken relation to: d
CHANGES
0.5 (2009-02-10)
Add support for RelationChoice field. To create a relation field that uses a drop-down list to select the relation target, implement a RelationSourceFactory (implement the getTargets method), and pass it as a source to RelationChoice.
0.4 (2009-01-20)
Use improved z3c.relationfield to better handle broken relations. Broken relations are now accepted by the UI but will be stored as broken.
0.3 (2009-01-16)
Pass along from_attribute and from_path URL parameters to explorer_url.
0.2 (2009-01-08)
Update the value of the input field using .value instead of using setAttribute('value', ...). The latter did not update dynamically updated input fields, and the former does.
z3c.relationfield does not use IRelationInfo anymore and instead exposes create_relation. Use this instead.
0.1.1 (2008-12-10)
Small internet Explorer compatibility tweaks.
0.1 (2008-12-05)
Initial public release.
Download
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.