Let’s show the different columns we offer by default. But first take a look at the README.txt which explains the Table and Column concepts.
Let’s create a sample container which we can use as our iterable context:
>>> from zope.app.container import btree
>>> class Container(btree.BTreeContainer):
... """Sample container."""
>>> container = Container()
>>> root['container'] = container
and create a sample content object which we use as container item:
>>> class Content(object):
... """Sample content."""
... def __init__(self, title, number):
... self.title = title
... self.number = number
Now setup some items:
>>> container[u'zero'] = Content('Zero', 0)
>>> container[u'first'] = Content('First', 1)
>>> container[u'second'] = Content('Second', 2)
>>> container[u'third'] = Content('Third', 3)
>>> container[u'fourth'] = Content('Fourth', 4)
Let’s also create a simple number sortable cloumn:
>>> from z3c.table import column
>>> class NumberColumn(column.Column):
...
... header = u'Number'
... weight = 20
...
... def getSortKey(self, item):
... return item.number
...
... def renderCell(self, item):
... return 'number: %s' % item.number
Let’s define a table using the NameColumn:
>>> from z3c.table import table
>>> class NameTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.NameColumn, u'name',
... weight=1),
... column.addColumn(self, NumberColumn, name=u'number',
... weight=2, header=u'Number')
... ]
Now create, update and render our table and you can see that the NameColumn renders the name of the item using the zope.traversing.api.getName() concept:
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> nameTable = NameTable(container, request)
>>> nameTable.update()
>>> print nameTable.render()
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td>first</td>
<td>number: 1</td>
</tr>
<tr>
<td>fourth</td>
<td>number: 4</td>
</tr>
<tr>
<td>second</td>
<td>number: 2</td>
</tr>
<tr>
<td>third</td>
<td>number: 3</td>
</tr>
<tr>
<td>zero</td>
<td>number: 0</td>
</tr>
</tbody>
</table>
Let’s define a table using the RadioColumn:
>>> class RadioTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.RadioColumn, u'radioColumn',
... weight=1),
... column.addColumn(self, NumberColumn, name=u'number',
... weight=2, header=u'Number')
... ]
Now create, update and render our table:
>>> request = TestRequest()
>>> radioTable = RadioTable(container, request)
>>> radioTable.update()
>>> print radioTable.render()
<table>
<thead>
<tr>
<th>X</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="first" /></td>
<td>number: 1</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="fourth" /></td>
<td>number: 4</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="second" /></td>
<td>number: 2</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="third" /></td>
<td>number: 3</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="zero" /></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
As you can see, we can force to render the radio input field as selected with a given request value:
>>> radioRequest = TestRequest(form={'table-radioColumn-0-selectedItem': 'third'})
>>> radioTable = RadioTable(container, radioRequest)
>>> radioTable.update()
>>> print radioTable.render()
<table>
<thead>
<tr>
<th>X</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="first" /></td>
<td>number: 1</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="fourth" /></td>
<td>number: 4</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="second" /></td>
<td>number: 2</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="third" checked="checked" /></td>
<td>number: 3</td>
</tr>
<tr>
<td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="zero" /></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
Let’s define a table using the RadioColumn:
>>> class CheckBoxTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.CheckBoxColumn, u'checkBoxColumn',
... weight=1),
... column.addColumn(self, NumberColumn, name=u'number',
... weight=2, header=u'Number')
... ]
Now create, update and render our table:
>>> request = TestRequest()
>>> checkBoxTable = CheckBoxTable(container, request)
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
<table>
<thead>
<tr>
<th>X</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" /></td>
<td>number: 1</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td>
<td>number: 4</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td>
<td>number: 2</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" /></td>
<td>number: 3</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
And again you can set force to render the checkbox input field as selected with a given request value:
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
... ['first', 'third']})
>>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
<table>
<thead>
<tr>
<th>X</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td>
<td>number: 1</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td>
<td>number: 4</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td>
<td>number: 2</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td>
<td>number: 3</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
If you select a row, you can also give them an additional CSS style. This could be used in combination with alternating even and odd styles:
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
... ['first', 'third']})
>>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
>>> checkBoxTable.cssClasses = {'tr': 'tr'}
>>> checkBoxTable.cssClassSelected = u'selected'
>>> checkBoxTable.cssClassEven = u'even'
>>> checkBoxTable.cssClassOdd = u'odd'
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
<table>
<thead>
<tr class="tr">
<th>X</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr class="selected even tr">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td>
<td>number: 1</td>
</tr>
<tr class="odd tr">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td>
<td>number: 4</td>
</tr>
<tr class="even tr">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td>
<td>number: 2</td>
</tr>
<tr class="selected odd tr">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td>
<td>number: 3</td>
</tr>
<tr class="even tr">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
Let’s test the cssClassSelected without any other css class
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
... ['first', 'third']})
>>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
>>> checkBoxTable.cssClassSelected = u'selected'
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
<table>
<thead>
<tr>
<th>X</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr class="selected">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td>
<td>number: 1</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td>
<td>number: 4</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td>
<td>number: 2</td>
</tr>
<tr class="selected">
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td>
<td>number: 3</td>
</tr>
<tr>
<td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
Let’s define a table using the CreatedColumn:
>>> class CreatedColumnTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.CreatedColumn, u'createdColumn',
... weight=1),
... ]
Now create, update and render our table. Note, we use a dublin core stub adapter which only returns 01/01/01 01:01 as created date:
>>> request = TestRequest()
>>> createdColumnTable = CreatedColumnTable(container, request)
>>> createdColumnTable.update()
>>> print createdColumnTable.render()
<table>
<thead>
<tr>
<th>Created</th>
</tr>
</thead>
<tbody>
<tr>
<td>01/01/01 01:01</td>
</tr>
<tr>
<td>01/01/01 01:01</td>
</tr>
<tr>
<td>01/01/01 01:01</td>
</tr>
<tr>
<td>01/01/01 01:01</td>
</tr>
<tr>
<td>01/01/01 01:01</td>
</tr>
</tbody>
</table>
Let’s define a table using the CreatedColumn:
>>> class ModifiedColumnTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.ModifiedColumn,
... u'modifiedColumn', weight=1),
... ]
Now create, update and render our table. Note, we use a dublin core stub adapter which only returns 02/02/02 02:02 as modified date:
>>> request = TestRequest()
>>> modifiedColumnTable = ModifiedColumnTable(container, request)
>>> modifiedColumnTable.update()
>>> print modifiedColumnTable.render()
<table>
<thead>
<tr>
<th>Modified</th>
</tr>
</thead>
<tbody>
<tr>
<td>02/02/02 02:02</td>
</tr>
<tr>
<td>02/02/02 02:02</td>
</tr>
<tr>
<td>02/02/02 02:02</td>
</tr>
<tr>
<td>02/02/02 02:02</td>
</tr>
<tr>
<td>02/02/02 02:02</td>
</tr>
</tbody>
</table>
The GetAttrColumn column is a mixin whihc is used in CreatedColumn and in ModifiedColumn. Not all code get used if everything is fine. So let’s test the column itself and force some usecase:
>>> class GetTitleColumn(column.GetAttrColumn): ... ... attrName = 'title' ... defaultValue = u'missing'>>> class GetAttrColumnTable(table.Table): ... ... attrName = 'title' ... defaultValue = u'missing' ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, GetTitleColumn, u'title'), ... ]
Render and update the table:
>>> request = TestRequest()
>>> getAttrColumnTable = GetAttrColumnTable(container, request)
>>> getAttrColumnTable.update()
>>> print getAttrColumnTable.render()
<table>
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>First</td>
</tr>
<tr>
<td>Fourth</td>
</tr>
<tr>
<td>Second</td>
</tr>
<tr>
<td>Third</td>
</tr>
<tr>
<td>Zero</td>
</tr>
</tbody>
</table>
If we use a none existing Attribute, we do not raise an AttributeError, we will get the default value defined from the GetAttrColumnTable
>>> class UndefinedAttributeColumn(column.GetAttrColumn): ... ... attrName = 'undefined' ... defaultValue = u'missing'>>> class GetAttrColumnTable(table.Table): ... ... attrName = 'title' ... defaultValue = u'missing' ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, UndefinedAttributeColumn, u'missing'), ... ]
Render and update the table:
>>> request = TestRequest()
>>> getAttrColumnTable = GetAttrColumnTable(container, request)
>>> getAttrColumnTable.update()
>>> print getAttrColumnTable.render()
<table>
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>missing</td>
</tr>
<tr>
<td>missing</td>
</tr>
<tr>
<td>missing</td>
</tr>
<tr>
<td>missing</td>
</tr>
<tr>
<td>missing</td>
</tr>
</tbody>
</table>
A missing attrName in GetAttrColumn whuold also end in return the defaultValue:
>>> class BadAttributeColumn(column.GetAttrColumn): ... ... defaultValue = u'missing'>>> firstItem = container[u'first'] >>> simpleTable = table.Table(container, request) >>> badColumn = column.addColumn(simpleTable, BadAttributeColumn, u'bad') >>> badColumn.renderCell(firstItem) u'missing'
If we try to access a protected attribute the object raises an Unauthorized. In thsi case we also return the defaultValue. Let’s setup an object which raises such an error if we access the title:
>>> from zope.security.interfaces import Unauthorized
>>> class ProtectedItem(object):
...
... @property
... def forbidden(self):
... raise Unauthorized, 'forbidden'
Setup and test the item:
>>> protectedItem = ProtectedItem()
>>> protectedItem.forbidden
...
Unauthorized: forbidden
Now define a column:
>>> class ForbiddenAttributeColumn(column.GetAttrColumn):
...
... attrName = 'forbidden'
... defaultValue = u'missing'
And test the attribute access:
>>> simpleTable = table.Table(container, request)
>>> badColumn = column.addColumn(simpleTable, ForbiddenAttributeColumn, u'x')
>>> badColumn.renderCell(protectedItem)
u'missing'
Let’s define a table using the LinkColumn. This column allows us to write columns which can point to a page with the item as context:
>>> class MyLinkColumns(column.LinkColumn): ... linkName = 'myLink.html' ... linkTarget = '_blank' ... linkCSS = 'myClass'>>> class MyLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, MyLinkColumns, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]
Now create, update and render our table:
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> myLinkTable = MyLinkTable(container, request)
>>> myLinkTable.__parent__ = container
>>> myLinkTable.__name__ = u'myLinkTable.html'
>>> myLinkTable.update()
>>> print myLinkTable.render()
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://127.0.0.1/container/first/myLink.html" target="_blank" class="myClass">first</a></td>
<td>number: 1</td>
</tr>
<tr>
<td><a href="http://127.0.0.1/container/fourth/myLink.html" target="_blank" class="myClass">fourth</a></td>
<td>number: 4</td>
</tr>
<tr>
<td><a href="http://127.0.0.1/container/second/myLink.html" target="_blank" class="myClass">second</a></td>
<td>number: 2</td>
</tr>
<tr>
<td><a href="http://127.0.0.1/container/third/myLink.html" target="_blank" class="myClass">third</a></td>
<td>number: 3</td>
</tr>
<tr>
<td><a href="http://127.0.0.1/container/zero/myLink.html" target="_blank" class="myClass">zero</a></td>
<td>number: 0</td>
</tr>
</tbody>
</table>
There are some predefined link columns available. This one will generate a contents.html link for each item:
>>> class ContentsLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.ContentsLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]>>> contentsLinkTable = ContentsLinkTable(container, request) >>> contentsLinkTable.__parent__ = container >>> contentsLinkTable.__name__ = u'contentsLinkTable.html' >>> contentsLinkTable.update() >>> print contentsLinkTable.render() <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/contents.html">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/contents.html">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/contents.html">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/contents.html">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/contents.html">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>
This one will generate a index.html link for each item:
>>> class IndexLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.IndexLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]>>> indexLinkTable = IndexLinkTable(container, request) >>> indexLinkTable.__parent__ = container >>> indexLinkTable.__name__ = u'indexLinkTable.html' >>> indexLinkTable.update() >>> print indexLinkTable.render() <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/index.html">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/index.html">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/index.html">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/index.html">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/index.html">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>
And this one will generate a edit.html link for each item:
>>> class EditLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.EditLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]>>> editLinkTable = EditLinkTable(container, request) >>> editLinkTable.__parent__ = container >>> editLinkTable.__name__ = u'editLinkTable.html' >>> editLinkTable.update() >>> print editLinkTable.render() <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/edit.html">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/edit.html">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/edit.html">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/edit.html">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/edit.html">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>