Column

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.

Sample data setup

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

NameColumn

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>

RadioColumn

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>

CheckBoxColumn

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>

CreatedColumn

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>

ModifiedColumn

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>

GetAttrColumn

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'

LinkColumn

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>

ContentsLinkColumn

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>

IndexLinkColumn

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>

EditLinkColumn

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>

Table Of Contents

Previous topic

Z3C Table

This Page