Различия между версиями 21 и 31 (по 10 версиям)
Версия 21 от 2010-07-13 13:47:58
Размер: 63141
Редактор: RostislavDzinko
Комментарий:
Версия 31 от 2010-07-14 12:27:34
Размер: 83511
Редактор: RostislavDzinko
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 5: Строка 5:
= Z3C Table = = Z3C Таблица =
Строка 9: Строка 9:
= Важные требования = == Важные требования ==
Строка 15: Строка 15:
= Никаких скинов = == Никаких скинов ==
Строка 19: Строка 19:
= Заметка = == Заметка ==
Строка 23: Строка 23:
= Пример установки данных = == Пример установки данных ==
Строка 60: Строка 60:
= Создание таблиц = == Создание таблиц ==
Строка 88: Строка 88:
= Создание столбцов = == Создание столбцов ==
Строка 105: Строка 105:
= Добавление колонки в таблицу с использованием адаптеров = == Добавление колонки в таблицу с использованием адаптеров ==
Строка 182: Строка 182:
= Объединение ячеек = == Объединение ячеек ==
Строка 268: Строка 268:
= Установка колонок = == Установка колонок ==
Строка 331: Строка 331:
= Каскадные таблицы стилей = == Каскадные таблицы стилей ==
Строка 391: Строка 391:
= Перемежающиеся таблицы = == Перемежающиеся таблицы ==
Строка 454: Строка 454:
= Сортировка таблицы = == Сортировка таблицы ==
Строка 673: Строка 673:
= Настройка таблицы на базе класса = == Настройка таблицы на базе класса ==
Строка 786: Строка 786:
= Порционное отображение данных = == Порционное отображение данных ==
Строка 1233: Строка 1233:
= BatchProvider = == BatchProvider ==
Строка 1423: Строка 1423:
= SequenceTable = == SequenceTable ==
Строка 1677: Строка 1677:
= Заголовки = == Заголовки ==
Строка 1734: Строка 1734:
= Другое = == Другое ==
Строка 1811: Строка 1811:
= Column (Столбец) =

Давайте покажем разницу между столбцами, которые мы предлагаем по умолчанию. Но сначала взгляните на '''README.txt''', который объясняет концепции '''Table''' (таблицы) и '''Column''' (колонки).

== Настройка тестовых данных ==

Давайте создадим тестовый контейнер, который сможем использовать как итерируемый контекст:

{{{#!highlight python
>>> from zope.app.container import btree
>>> class Container(btree.BTreeContainer):
... """Sample container."""
>>> container = Container()
>>> root['container'] = container
}}}

и создадим тестовый контент объект, который будем использовать как содержимое контейнера:

{{{#!highlight python
>>> class Content(object):
... """Sample content."""
... def __init__(self, title, number):
... self.title = title
... self.number = number
}}}

Теперь добавим несколько объектов в контейнер:

{{{#!highlight python
>>> 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)
}}}

Давайте также создадим простую сортируемую колонку:

{{{#!highlight python
>>> 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 ==

Давайте определим таблицу с использованием '''!NameColumn''':

{{{#!highlight python:
>>> 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')
... ]
}}}

Теперь создадим, обновим и отобразим таблицу и вы увидите, что '''!NameColumn''' отображает имя объекта, используя метод '''zope.traversing.api.getName()''':

{{{#!highlight python
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> nameTable = NameTable(container, request)
>>> nameTable.update()
>>> print nameTable.render()
}}}
{{{#!highlight html
<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 ==

Давайте определим таблицу используя '''!RadioColumn''':

{{{#!highlight python
>>> 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')
... ]
}}}

Теперь создадим, обновим и отобразим таблицу:

{{{#!highlight python
>>> request = TestRequest()
>>> radioTable = RadioTable(container, request)
>>> radioTable.update()
>>> print radioTable.render()
}}}
{{{#!highlight html
<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>
}}}

Как видите, мы можем насильно отобразить поле опционного ввода, как выбрано в значении, что передается через переменную запроса:

{{{#!highlight python
>>> radioRequest = TestRequest(form={'table-radioColumn-0-selectedItem': 'third'})
>>> radioTable = RadioTable(container, radioRequest)
>>> radioTable.update()
>>> print radioTable.render()
}}}
{{{#!highlight html
<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 ==

Давайте определим таблицу с использованием '''!CheckBoxColumn''':

{{{#!highlight python
>>> 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')
... ]
}}}

Теперь создадим, обновим и отобразим таблицу:

{{{#!highlight python
>>> request = TestRequest()
>>> checkBoxTable = CheckBoxTable(container, request)
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
}}}
{{{#!highlight html
<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>
}}}

И снова вы можете насильно отобразить поле ввода с галочкой, как указано в переменной запроса:

{{{#!highlight python
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
... ['first', 'third']})
>>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
}}}
{{{#!highlight html
<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>
}}}

Если вы выбираете строку, вы также можете предоставить дополнительный CSS стиль. Это может быть использовано при создании перемежающихся строк:

{{{#!highlight python
>>> 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()
}}}
{{{#!highlight html
<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>
}}}

Давайте протестируем '''cssClassSelected''' без других css классов

{{{#!highlight python
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
... ['first', 'third']})
>>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
>>> checkBoxTable.cssClassSelected = u'selected'
>>> checkBoxTable.update()
>>> print checkBoxTable.render()
}}}
{{{#!highlight html
<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 ==

Давайте определим таблицу с использованием '''!CreatedColumn''':

{{{#!highlight python
>>> class CreatedColumnTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.CreatedColumn, u'createdColumn',
... weight=1),
... ]
}}}

Теперь создадим, обновим и отобразим таблицу. Заметьте, что мы используем тестовый адаптер к dublin core, который возвращает 01/01/01 01:01 в качестве даты создания:

{{{#!highlight python
>>> request = TestRequest()
>>> createdColumnTable = CreatedColumnTable(container, request)
>>> createdColumnTable.update()
>>> print createdColumnTable.render()
}}}
{{{#!highlight html
<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 ==

Давайте определим таблицу используя '''!ModifiedColumn''':

{{{#!highlight python
>>> class ModifiedColumnTable(table.Table):
...
... def setUpColumns(self):
... return [
... column.addColumn(self, column.ModifiedColumn,
... u'modifiedColumn', weight=1),
... ]
}}}

Теперь создадим, обновим и отобразим таблицу. Заметьте, мы использовали тестовый адаптер dublin core, который возвращает 02/02/02 02:02 в качестве даты модификации:

{{{#!highlight python
>>> request = TestRequest()
>>> modifiedColumnTable = ModifiedColumnTable(container, request)
>>> modifiedColumnTable.update()
>>> print modifiedColumnTable.render()
}}}
{{{#!highlight html
<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 ==

Столбец '''!GetAttrColumn''' - это помесь (mixin), которая используется в '''!CreatedColumn''' и в '''!ModifiedColumn'''. Если все хорошо, то используется не весь код. Давайте протестируем эту колонку саму по себе:

{{{#!highlight python
>>> 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'),
... ]
}}}

Обновите и отобразите таблицу:

{{{#!highlight python
>>> request = TestRequest()
>>> getAttrColumnTable = GetAttrColumnTable(container, request)
>>> getAttrColumnTable.update()
>>> print getAttrColumnTable.render()
}}}
{{{#!highlight html
<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>
}}}

Если мы используем не существующий атрибут, то не получаете '''!AttributeError''', а значение по умолчанию, которое определено в '''!GetAttrColumnTable'''.

{{{#!highlight python
>>> 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'),
... ]
}}}

Обновите и отобразите таблицу:

{{{#!highlight python
>>> request = TestRequest()
>>> getAttrColumnTable = GetAttrColumnTable(container, request)
>>> getAttrColumnTable.update()
>>> print getAttrColumnTable.render()
}}}
{{{#!hihghlight html
<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>
}}}

Несуществующий '''attrName''' в '''GetAttrColumn''' также вернет '''defaultValue''':

{{{#!highlight python
>>> 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'
}}}

Если мы попытаемся получить доступ к защищенному атрибуту, объект вызовет исключение '''Unauthorized'''. В этом случае вы также получите '''defaultValue'''. Давайте установим объект, который вызывает такую ошибку при попытке доступа к атрибуту '''title''':

{{{#!highlight python
>>> from zope.security.interfaces import Unauthorized
>>> class ProtectedItem(object):
...
... @property
... def forbidden(self):
... raise Unauthorized, 'forbidden'
}}}

Установите и протестируйте объект:

{{{#!highlight python
>>> protectedItem = ProtectedItem()
>>> protectedItem.forbidden
...
Unauthorized: forbidden
}}}

Теперь определим столбец:

{{{#!highlight python
>>> 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'
}}}

z3c.table - продвинутые таблицы

Z3C Таблица

Цель, которую преследует пакет z3c.table - предложить модульную библиотеку для отрисовки таблиц. Мы используем шаблон "контент провайдер" с колонками, реализованными как адаптеры. Такой подход - мощная базовая концепция.

Важные требования

  • разделение реализации в обновлении частей отрисовки. Такой подход позволяет манипулировать данными после обновления, и перед отрисовкой.
  • мы можем использовать шаблоны страниц, если потребуется, но, по умолчанию, все делается в Python.
  • мы может отрисовывать порционную навигацию отдельно от самой таблицы.

Никаких скинов

Этот пакет не предоставляет никаких шаблонов и скинов. В любом случае, когда вам нужно отрисовать красивую таблицу, вам придется писать свой собственный скин или шаблон. Отсутствие шаблонов и скинов позволяет удостоверится, что z3c.table имеет очень мало зависимостей, а поэтому легко поддается повторному использованию.

Заметка

Как вы, вероятно, уже знаете, перед тем, как выполняется сортировка по столбцам при порционном отображении данных таблицы, должна быть выполнена сортировка полного набора данных. При большом наборе данных это ведет к проблемам с быстродействием. При работе с большими объемами данных мы рекомендуем не совмещать порционное отображение с сортировкой по столбцам, либо, если нужно, предоставлять "умный" механизм кеширования данных для хранения отсортированных последовательностей.

Пример установки данных

Таблицы часто используются для отображения нормализованных порций данных. Например, нам необходимо отобразить информацию о файлах в папке. Каждый файл имеет заголовок, размер и тип. Наша таблица должна иметь строку для каждого файла и столбец для этих трех полей данных (заголовка, размера, и типа). Контекстом таблицы всегда является структура данных, по которой можно выполнить итерацию, при этом каждый элемент последовательности соответствует строке таблицы. Давайте создадим папку, которую мы сможем использовать как контекст:

Переключить отображение номеров строк
   1 >>> from zope.app.container import btree
   2 >>> class Folder(btree.BTreeContainer):
   3 ...     """Sample folder."""
   4 ...     __name__ = u'folder'
   5 >>> folder = Folder()

XXX: не уверен, куда нам нужно положить эту папку. Также нам можно и не давать значение атрибуту _ _name_ _. Нам не нужно куда либо помещать ее. Давайте установим родительский элемент для папки:

Переключить отображение номеров строк
   1 >>> root['folder'] = folder

Теперь создадим простой объект File для наполнения созданной нами папки.

Переключить отображение номеров строк
   1 >>> class File(object):
   2 ...     """Sample file."""
   3 ...     def __init__(self, title, size, type=None):
   4 ...         self.title = title
   5 ...         self.number = size
   6 ...         self.type = type

Теперь давайте наполним созданную папку файлами.

Переключить отображение номеров строк
   1 >>> folder[u'first'] = File('First', 1)
   2 >>> folder[u'second'] = File('Second', 2)
   3 >>> folder[u'third'] = File('Third', 3)

Создание таблиц

Теперь, когда у нас есть тестовые данные, с которыми можно работать, мы может создать таблицу. Так как таблицы - компоненты пользовательского интерфейса, они требуют и контекста и запроса (request). Они передаются как аргументы конструктору класса Table.

Переключить отображение номеров строк
   1 >>> from zope.publisher.browser import TestRequest
   2 >>> from z3c.table import table
   3 >>> request = TestRequest()
   4 >>> plainTable = table.Table(folder, request)

Когда таблица создана, мы можем ее обновлять и отрисовывать. Так как мы не указали столбцы таблицы, которые нужно отрисовать, таблица отрисуется как пустая строка:

Переключить отображение номеров строк
   1 >>> plainTable.update()
   2 >>> plainTable.render()
   3 u''

Также стоит заметить, что класс Table - реализация интерфейса ITable. Намного интереснее взглянуть, что предоставляет ITable, когда у нас есть несколько столбцов:

Переключить отображение номеров строк
   1 >>> from z3c.table import interfaces
   2 >>> from zope.interface.verify import verifyObject
   3 >>> verifyObject(interfaces.ITable, plainTable)
   4 True

Создание столбцов

Так как нам может понадобится некий тип столбцов в многих разных таблицах, определение столбца лежит отдельно от самой таблицы. Каждый тип столбцов представлен собственным классом, который реализует интерфейс IColumn. Чтобы помочь в определении столбца, существует базовый класс Column. Простая колонка, которая отображает заголовок элемента выглядит примерно так:

Переключить отображение номеров строк
   1 >>> from z3c.table import column
   2 >>> class TitleColumn(column.Column):
   3 ...
   4 ...     weight = 10
   5 ...     header = u'Title'
   6 ...
   7 ...     def renderCell(self, item):
   8 ...         return u'Title: %s' % item.title

Атрибут header - это текст, который мы можем увидеть в заголовке таблицы, обычно это тег <th>. Атрибут weight указывает порядок колонки в таблице относительно других колонок. Метод renderCell делает всю работу по отображению, возвращая html структуру, которая будет находится внутри колонки таблица, обычно это тег <td>. Метод renderCell должен предоставляться потомками класса Column.

Добавление колонки в таблицу с использованием адаптеров

Одним из способов добавления колонки в таблицу является адаптер. Хотя сначала такой подход кажется избыточным, он делает таблицы легко подключаемыми. Давайте зарегистрируем колонку как адаптер.

Переключить отображение номеров строк
   1 >>> import zope.component
   2 >>> zope.component.provideAdapter(TitleColumn,
   3 ...     (None, None, interfaces.ITable), provides=interfaces.IColumn,
   4 ...      name='firstColumn')

Теперь отрисуем таблицу еще раз:

Переключить отображение номеров строк
   1 >>> plainTable.update()
   2 >>> print plainTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Title</th>
   5     </tr>
   6   </thead>
   7   <tbody>
   8     <tr>
   9       <td>Title: First</td>
  10     </tr>
  11     <tr>
  12       <td>Title: Second</td>
  13     </tr>
  14     <tr>
  15       <td>Title: Third</td>
  16     </tr>
  17   </tbody>
  18 </table>

Мы также можем использовать предопределенное имя столбца:

Переключить отображение номеров строк
   1 >>> zope.component.provideAdapter(column.NameColumn,
   2 ...     (None, None, interfaces.ITable), provides=interfaces.IColumn,
   3 ...      name='secondColumn')

Теперь мы получим еще один дополнительный столбец:

Переключить отображение номеров строк
   1 >>> plainTable.update()
   2 >>> print plainTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Name</th>
   5       <th>Title</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>first</td>
  11       <td>Title: First</td>
  12     </tr>
  13     <tr>
  14       <td>second</td>
  15       <td>Title: Second</td>
  16     </tr>
  17     <tr>
  18       <td>third</td>
  19       <td>Title: Third</td>
  20     </tr>
  21   </tbody>
  22 </table>

Объединение ячеек

Теперь давайте посмотрим, как можно сделать объединение ячеек для столбца:

Переключить отображение номеров строк
   1 >>> class ColspanColumn(column.NameColumn):
   2 ...
   3 ...     weight = 999
   4 ...
   5 ...     def getColspan(self, item):
   6 ...         # colspan condition
   7 ...         if item.__name__ == 'first':
   8 ...             return 2
   9 ...         else:
  10 ...             return 0
  11 ...
  12 ...     def renderHeadCell(self):
  13 ...         return u'Colspan'
  14 ...
  15 ...     def renderCell(self, item):
  16 ...         return u'colspan: %s' % item.title

Теперь зарегистрируем адаптер этого столбца как colspanColumn:

Переключить отображение номеров строк
   1 >>> zope.component.provideAdapter(ColspanColumn,
   2 ...     (None, None, interfaces.ITable), provides=interfaces.IColumn,
   3 ...      name='colspanColumn')

Теперь вы видите, насколько объединение через ColspanAdapter больше, чем столбцы таблицы. Такой код вызовет исключение ValueError:

Переключить отображение номеров строк
   1 >>> plainTable.update()
   2 ...
   3 ValueError: Colspan for column '<ColspanColumn u'colspanColumn'>' larger then table.

Но если мы установим столбец первой строчкой, таблица отрисуется корректно:

Переключить отображение номеров строк
   1 >>> class CorrectColspanColumn(ColspanColumn):
   2 ...     """Colspan with correct weight."""
   3 ...
   4 ...     weight = 0

Зарегистрируйте и отрисуйте таблицу еще раз:

Переключить отображение номеров строк
   1 >>> zope.component.provideAdapter(CorrectColspanColumn,
   2 ...     (None, None, interfaces.ITable), provides=interfaces.IColumn,
   3 ...      name='colspanColumn')
   4 >>> plainTable.update()
   5 >>> print plainTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Colspan</th>
   5       <th>Name</th>
   6       <th>Title</th>
   7     </tr>
   8   </thead>
   9   <tbody>
  10     <tr>
  11       <td colspan="2">colspan: First</td>
  12       <td>Title: First</td>
  13     </tr>
  14     <tr>
  15       <td>colspan: Second</td>
  16       <td>second</td>
  17       <td>Title: Second</td>
  18     </tr>
  19     <tr>
  20       <td>colspan: Third</td>
  21       <td>third</td>
  22       <td>Title: Third</td>
  23     </tr>
  24   </tbody>
  25 </table>

Установка колонок

Существующая реализация позволяет определять таблицу в классе без использования адаптеров.

Сначала нужно определить столбец, который сможет отобразить значения наших элементов:

Переключить отображение номеров строк
   1 >>> class SimpleColumn(column.Column):
   2 ...
   3 ...     weight = 0
   4 ...
   5 ...     def renderCell(self, item):
   6 ...         return item.title

Давайте определим нашу таблицу, которая явно определяет столбцы. Вы также можете увидеть, что мы не возвращаем колонки в правильном порядке:

Переключить отображение номеров строк
   1 >>> class PrivateTable(table.Table):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         firstColumn = TitleColumn(self.context, self.request, self)
   5 ...         firstColumn.__name__ = u'title'
   6 ...         firstColumn.weight = 1
   7 ...         secondColumn = SimpleColumn(self.context, self.request, self)
   8 ...         secondColumn.__name__ = u'simple'
   9 ...         secondColumn.weight = 2
  10 ...         secondColumn.header = u'The second column'
  11 ...         return [secondColumn, firstColumn]

Теперь мы можете создавать, обновлять и отображать таблицу:

Переключить отображение номеров строк
   1 >>> privateTable = PrivateTable(folder, request)
   2 >>> privateTable.update()
   3 >>> print privateTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Title</th>
   5       <th>The second column</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Title: First</td>
  11       <td>First</td>
  12     </tr>
  13     <tr>
  14       <td>Title: Second</td>
  15       <td>Second</td>
  16     </tr>
  17     <tr>
  18       <td>Title: Third</td>
  19       <td>Third</td>
  20     </tr>
  21   </tbody>
  22 </table>

Каскадные таблицы стилей

Наша реализация таблицы и столбца поддерживает установку классов css. Давайте определим таблицу и столбцы с некими значениями css:

Переключить отображение номеров строк
   1 >>> class CSSTable(table.Table):
   2 ...
   3 ...     cssClasses = {'table': 'table',
   4 ...                   'thead': 'thead',
   5 ...                   'tbody': 'tbody',
   6 ...                   'th': 'th',
   7 ...                   'tr': 'tr',
   8 ...                   'td': 'td'}
   9 ...
  10 ...     def setUpColumns(self):
  11 ...         firstColumn = TitleColumn(self.context, self.request, self)
  12 ...         firstColumn.__name__ = u'title'
  13 ...         firstColumn.__parent__ = self
  14 ...         firstColumn.weight = 1
  15 ...         firstColumn.cssClasses = {'th':'thCol', 'td':'tdCol'}
  16 ...         secondColumn = SimpleColumn(self.context, self.request, self)
  17 ...         secondColumn.__name__ = u'simple'
  18 ...         secondColumn.__parent__ = self
  19 ...         secondColumn.weight = 2
  20 ...         secondColumn.header = u'The second column'
  21 ...         return [secondColumn, firstColumn]

Теперь посмотрит, как отображается такая таблица с учетом присвоенных значений css классов. Заметьте, что th и td получили из таблицы и из колонки.

Переключить отображение номеров строк
   1 >>> cssTable = CSSTable(folder, request)
   2 >>> cssTable.update()
   3 >>> print cssTable.render()

Переключить отображение номеров строк
   1 <table class="table">
   2   <thead class="thead">
   3     <tr class="tr">
   4       <th class="thCol th">Title</th>
   5       <th class="th">The second column</th>
   6     </tr>
   7   </thead>
   8   <tbody class="tbody">
   9     <tr class="tr">
  10       <td class="tdCol td">Title: First</td>
  11       <td class="td">First</td>
  12     </tr>
  13     <tr class="tr">
  14       <td class="tdCol td">Title: Second</td>
  15       <td class="td">Second</td>
  16     </tr>
  17     <tr class="tr">
  18       <td class="tdCol td">Title: Third</td>
  19       <td class="td">Third</td>
  20     </tr>
  21   </tbody>
  22 </table>

Перемежающиеся таблицы

Мы поддерживаем встроенную поддержку перемежающихся строк таблицы, которые основаны на парных и непарных классах CSS. Давайте определим таблицу, включая другие CSS классы. Для поддержки парности/непарности нам следует определить классу cssClassEven и cssClassOdd CSS:

Переключить отображение номеров строк
   1 >>> class AlternatingTable(table.Table):
   2 ...
   3 ...     cssClasses = {'table': 'table',
   4 ...                   'thead': 'thead',
   5 ...                   'tbody': 'tbody',
   6 ...                   'th': 'th',
   7 ...                   'tr': 'tr',
   8 ...                   'td': 'td'}
   9 ...
  10 ...     cssClassEven = u'even'
  11 ...     cssClassOdd = u'odd'
  12 ...
  13 ...     def setUpColumns(self):
  14 ...         firstColumn = TitleColumn(self.context, self.request, self)
  15 ...         firstColumn.__name__ = u'title'
  16 ...         firstColumn.__parent__ = self
  17 ...         firstColumn.weight = 1
  18 ...         firstColumn.cssClasses = {'th':'thCol', 'td':'tdCol'}
  19 ...         secondColumn = SimpleColumn(self.context, self.request, self)
  20 ...         secondColumn.__name__ = u'simple'
  21 ...         secondColumn.__parent__ = self
  22 ...         secondColumn.weight = 2
  23 ...         secondColumn.header = u'The second column'
  24 ...         return [secondColumn, firstColumn]

Теперь обновите и отобразите новую таблицу. Как видите, к данному tr классу добавились дополнительные классы even и odd:

Переключить отображение номеров строк
   1 >>> alternatingTable = AlternatingTable(folder, request)
   2 >>> alternatingTable.update()
   3 >>> print alternatingTable.render()

Переключить отображение номеров строк
   1 <table class="table">
   2   <thead class="thead">
   3     <tr class="tr">
   4       <th class="thCol th">Title</th>
   5       <th class="th">The second column</th>
   6     </tr>
   7   </thead>
   8   <tbody class="tbody">
   9     <tr class="even tr">
  10       <td class="tdCol td">Title: First</td>
  11       <td class="td">First</td>
  12     </tr>
  13     <tr class="odd tr">
  14       <td class="tdCol td">Title: Second</td>
  15       <td class="td">Second</td>
  16     </tr>
  17     <tr class="even tr">
  18       <td class="tdCol td">Title: Third</td>
  19       <td class="td">Third</td>
  20     </tr>
  21   </tbody>
  22 </table>

Сортировка таблицы

Еще одно свойство таблицы - поддержка сортировки данных по столбцам. Так как сортировка данных таблицы вещь очень важная, мы предлагаем ее по умолчанию. Но ее можно использовать только тогда, когда установлено значение sortOn. Вы можете установить это значение на уровне класса, добавляя значение defaultSortOn или устанавливая его как значение в запросе. Мы покажем вам, как это сделать позже. Нам также потребуются столбцы, которые помогут продемонстрировать хороший пример сортировки. Наш новый столбец, по которому будем сортировать, будет использовать атрибут number элементов содержимого в качестве критерия сортировки:

Переключить отображение номеров строк
   1 >>> class NumberColumn(column.Column):
   2 ...
   3 ...     header = u'Number'
   4 ...     weight = 20
   5 ...
   6 ...     def getSortKey(self, item):
   7 ...         return item.number
   8 ...
   9 ...     def renderCell(self, item):
  10 ...         return 'number: %s' % item.number

Теперь давайте установим таблицу:

Переключить отображение номеров строк
   1 >>> class SortingTable(table.Table):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         firstColumn = TitleColumn(self.context, self.request, self)
   5 ...         firstColumn.__name__ = u'title'
   6 ...         firstColumn.__parent__ = self
   7 ...         secondColumn = NumberColumn(self.context, self.request, self)
   8 ...         secondColumn.__name__ = u'number'
   9 ...         secondColumn.__parent__ = self
  10 ...         return [firstColumn, secondColumn]

Нам также потребуется больше элементов в контейнере, которые мы будем сортировать:

Переключить отображение номеров строк
   1 >>> folder[u'fourth'] = File('Fourth', 4)
   2 >>> folder[u'zero'] = File('Zero', 0)

Давайте отобразим из без установленного значения sortOn:

Переключить отображение номеров строк
   1 >>> sortingTable = SortingTable(folder, request)
   2 >>> sortingTable.update()
   3 >>> print sortingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Title</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Title: First</td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td>Title: Fourth</td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td>Title: Second</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Title: Third</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>Title: Zero</td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

Как видите, эта таблица не предоставляет никакого явного порядка. Давайте определим индекс столбца, по которому будем сортировать данные:

Переключить отображение номеров строк
   1 >>> sortOnId = sortingTable.rows[0][1][1].id
   2 >>> sortOnId
   3 u'table-number-1'

Теперь давайте используем найденный индекс как значение sortOn:

Переключить отображение номеров строк
   1 >>> sortingTable.sortOn = sortOnId

Важным моментом является обновление таблицы после установки сортировки по значению:

Переключить отображение номеров строк
   1 >>> sortingTable.update()
   2 >>> print sortingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Title</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Title: Zero</td>
  11       <td>number: 0</td>
  12     </tr>
  13     <tr>
  14       <td>Title: First</td>
  15       <td>number: 1</td>
  16     </tr>
  17     <tr>
  18       <td>Title: Second</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Title: Third</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>Title: Fourth</td>
  27       <td>number: 4</td>
  28     </tr>
  29   </tbody>
  30 </table>

Мы также можем инвертировать порядок сортировки:

Переключить отображение номеров строк
   1 >>> sortingTable.sortOrder = 'reverse'
   2 >>> sortingTable.update()
   3 >>> print sortingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Title</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Title: Fourth</td>
  11       <td>number: 4</td>
  12     </tr>
  13     <tr>
  14       <td>Title: Third</td>
  15       <td>number: 3</td>
  16     </tr>
  17     <tr>
  18       <td>Title: Second</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Title: First</td>
  23       <td>number: 1</td>
  24     </tr>
  25     <tr>
  26       <td>Title: Zero</td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

Реализация таблицы также позволяет установить критерий сортировки, который передается через запрос. Давайте настроим такой запрос:

Переключить отображение номеров строк
   1 >>> sorterRequest = TestRequest(form={'table-sortOn': 'table-number-1',
   2 ...                                   'table-sortOrder':'descending'})

и еще раз обновим и отобразим. Как видите, новая таблица отсортирована по втором столбце и упорядоченна в инверсном порядке:

{{{#highlight python >>> requestSortedTable = SortingTable(folder, sorterRequest) >>> requestSortedTable.update() >>> print requestSortedTable.render() }}}

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Title</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Title: Fourth</td>
  11       <td>number: 4</td>
  12     </tr>
  13     <tr>
  14       <td>Title: Third</td>
  15       <td>number: 3</td>
  16     </tr>
  17     <tr>
  18       <td>Title: Second</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Title: First</td>
  23       <td>number: 1</td>
  24     </tr>
  25     <tr>
  26       <td>Title: Zero</td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

Настройка таблицы на базе класса

Есть еще один красивый способ определить строки таблицы на уровне класса. Мы предлагаем метод, который вы можете использовать, если нужно определить несколько колонок, - это метод addColumn. Перед тем, как определить таблицу, давайте определим метод отображения ячеек:

Переключить отображение номеров строк
   1 >>> def headCellRenderer():
   2 ...     return u'My items'
   3 >>> def cellRenderer(item):
   4 ...     return u'%s item' % item.title

Теперь мы можем определить нашу таблицу и использовать свой метод рисования ячеек:

Переключить отображение номеров строк
   1 >>> class AddColumnTable(table.Table):
   2 ...
   3 ...     cssClasses = {'table': 'table',
   4 ...                   'thead': 'thead',
   5 ...                   'tbody': 'tbody',
   6 ...                   'th': 'th',
   7 ...                   'tr': 'tr',
   8 ...                   'td': 'td'}
   9 ...
  10 ...     cssClassEven = u'even'
  11 ...     cssClassOdd = u'odd'
  12 ...
  13 ...     def setUpColumns(self):
  14 ...         return [
  15 ...             column.addColumn(self, TitleColumn, u'title',
  16 ...                              cellRenderer=cellRenderer,
  17 ...                              headCellRenderer=headCellRenderer,
  18 ...                              weight=1, colspan=0),
  19 ...             column.addColumn(self, SimpleColumn, name=u'simple',
  20 ...                              weight=2, header=u'The second column',
  21 ...                              cssClasses = {'th':'thCol', 'td':'tdCol'})
  22 ...             ]
  23 >>> addColumnTable = AddColumnTable(folder, request)
  24 >>> addColumnTable.update()
  25 >>> print addColumnTable.render()

Переключить отображение номеров строк
   1 <table class="table">
   2   <thead class="thead">
   3     <tr class="tr">
   4       <th class="th">My items</th>
   5       <th class="thCol th">The second column</th>
   6     </tr>
   7   </thead>
   8   <tbody class="tbody">
   9     <tr class="even tr">
  10       <td class="td">First item</td>
  11       <td class="tdCol td">First</td>
  12     </tr>
  13     <tr class="odd tr">
  14       <td class="td">Fourth item</td>
  15       <td class="tdCol td">Fourth</td>
  16     </tr>
  17     <tr class="even tr">
  18       <td class="td">Second item</td>
  19       <td class="tdCol td">Second</td>
  20     </tr>
  21     <tr class="odd tr">
  22       <td class="td">Third item</td>
  23       <td class="tdCol td">Third</td>
  24     </tr>
  25     <tr class="even tr">
  26       <td class="td">Zero item</td>
  27       <td class="tdCol td">Zero</td>
  28     </tr>
  29   </tbody>
  30 </table>

Как видите, столбцы таблицы предоставляют все атрибуты, которые были установлены в методе addColumn:

Переключить отображение номеров строк
   1 >>> titleColumn = addColumnTable.rows[0][0][1]
   2 >>> titleColumn
   3 <TitleColumn u'title'>
   4 >>> titleColumn.__name__
   5 u'title'
   6 >>> titleColumn.__parent__
   7 <AddColumnTable None>
   8 >>> titleColumn.colspan
   9 0
  10 >>> titleColumn.weight
  11 1
  12 >>> titleColumn.header
  13 u'Title'
  14 >>> titleColumn.cssClasses
  15 {}

и вторая колонка

Переключить отображение номеров строк
   1 >>> simpleColumn = addColumnTable.rows[0][1][1]
   2 >>> simpleColumn
   3 <SimpleColumn u'simple'>
   4 >>> simpleColumn.__name__
   5 u'simple'
   6 >>> simpleColumn.__parent__
   7 <AddColumnTable None>
   8 >>> simpleColumn.colspan
   9 0
  10 >>> simpleColumn.weight
  11 2
  12 >>> simpleColumn.header
  13 u'The second column'
  14 >>> simpleColumn.cssClasses
  15 {'td': 'tdCol', 'th': 'thCol'}

Порционное отображение данных

Таблица реализует концепцию порционного отображения данных из коробки. Если количество строк меньше заданного размера startBatchingAt, таблица начнет разбиваться на этом размере. Давайте определим новую таблицу:

Следующим шагом идет настройка провайдера порций. Посмотрите раздел BatchProvider (ниже) для получения более подробной информации о порционном отображении данных:

Переключить отображение номеров строк
   1 >>> from zope.configuration.xmlconfig import XMLConfig
   2 >>> import zope.app.component
   3 >>> import z3c.table
   4 >>> XMLConfig('meta.zcml', zope.component)()
   5 >>> XMLConfig('configure.zcml', z3c.table)()
   6 >>> class BatchingTable(table.Table):
   7 ...
   8 ...     def setUpColumns(self):
   9 ...         return [
  10 ...             column.addColumn(self, TitleColumn, u'title',
  11 ...                              cellRenderer=cellRenderer,
  12 ...                              headCellRenderer=headCellRenderer,
  13 ...                              weight=1),
  14 ...             column.addColumn(self, NumberColumn, name=u'number',
  15 ...                              weight=2, header=u'Number')
  16 ...             ]

Теперь мы можем создать таблицу:

Переключить отображение номеров строк
   1 >>> batchingTable = BatchingTable(folder, request)

Нам также следует дать таблице расположение и имя, как принято при траверсинге:

Переключить отображение номеров строк
   1 >>> batchingTable.__parent__ = folder
   2 >>> batchingTable.__name__ = u'batchingTable.html'

Теперь добавим еще несколько элементов в папку:

Переключить отображение номеров строк
   1 >>> folder[u'sixth'] = File('Sixth', 6)
   2 >>> folder[u'seventh'] = File('Seventh', 7)
   3 >>> folder[u'eighth'] = File('Eighth', 8)
   4 >>> folder[u'ninth'] = File('Ninth', 9)
   5 >>> folder[u'tenth'] = File('Tenth', 10)
   6 >>> folder[u'eleventh'] = File('Eleventh', 11)
   7 >>> folder[u'twelfth '] = File('Twelfth', 12)
   8 >>> folder[u'thirteenth'] = File('Thirteenth', 13)
   9 >>> folder[u'fourteenth'] = File('Fourteenth', 14)
  10 >>> folder[u'fifteenth '] = File('Fifteenth', 15)
  11 >>> folder[u'sixteenth'] = File('Sixteenth', 16)
  12 >>> folder[u'seventeenth'] = File('Seventeenth', 17)
  13 >>> folder[u'eighteenth'] = File('Eighteenth', 18)
  14 >>> folder[u'nineteenth'] = File('Nineteenth', 19)
  15 >>> folder[u'twentieth'] = File('Twentieth', 20)

Теперь покажем полную таблицу:

Переключить отображение номеров строк
   1 >>> batchingTable.update()
   2 >>> print batchingTable.render()
   3 <table>
   4   <thead>
   5     <tr>
   6       <th>My items</th>
   7       <th>Number</th>
   8     </tr>
   9   </thead>
  10   <tbody>
  11     <tr>
  12       <td>Eighteenth item</td>
  13       <td>number: 18</td>
  14     </tr>
  15     <tr>
  16       <td>Eighth item</td>
  17       <td>number: 8</td>
  18     </tr>
  19     <tr>
  20       <td>Eleventh item</td>
  21       <td>number: 11</td>
  22     </tr>
  23     <tr>
  24       <td>Fifteenth item</td>
  25       <td>number: 15</td>
  26     </tr>
  27     <tr>
  28       <td>First item</td>
  29       <td>number: 1</td>
  30     </tr>
  31     <tr>
  32       <td>Fourteenth item</td>
  33       <td>number: 14</td>
  34     </tr>
  35     <tr>
  36       <td>Fourth item</td>
  37       <td>number: 4</td>
  38     </tr>
  39     <tr>
  40       <td>Nineteenth item</td>
  41       <td>number: 19</td>
  42     </tr>
  43     <tr>
  44       <td>Ninth item</td>
  45       <td>number: 9</td>
  46     </tr>
  47     <tr>
  48       <td>Second item</td>
  49       <td>number: 2</td>
  50     </tr>
  51     <tr>
  52       <td>Seventeenth item</td>
  53       <td>number: 17</td>
  54     </tr>
  55     <tr>
  56       <td>Seventh item</td>
  57       <td>number: 7</td>
  58     </tr>
  59     <tr>
  60       <td>Sixteenth item</td>
  61       <td>number: 16</td>
  62     </tr>
  63     <tr>
  64       <td>Sixth item</td>
  65       <td>number: 6</td>
  66     </tr>
  67     <tr>
  68       <td>Tenth item</td>
  69       <td>number: 10</td>
  70     </tr>
  71     <tr>
  72       <td>Third item</td>
  73       <td>number: 3</td>
  74     </tr>
  75     <tr>
  76       <td>Thirteenth item</td>
  77       <td>number: 13</td>
  78     </tr>
  79     <tr>
  80       <td>Twelfth item</td>
  81       <td>number: 12</td>
  82     </tr>
  83     <tr>
  84       <td>Twentieth item</td>
  85       <td>number: 20</td>
  86     </tr>
  87     <tr>
  88       <td>Zero item</td>
  89       <td>number: 0</td>
  90     </tr>
  91   </tbody>
  92 </table>

Как видите, таблица неупорядоченная и отображает все элементы. Если мы хотим разбить ее на порции, нам следует установить размер startBatchingAt меньшим, нежели он установлен по умолчанию. Значение по умолчанию - 50:

Переключить отображение номеров строк
   1 >>> batchingTable.startBatchingAt
   2 50

Мы начнем порцию на 5 элементе. Это значит, что первые 5 элементов не будут использоваться:

Переключить отображение номеров строк
   1 >>> batchingTable.startBatchingAt = 5
   2 >>> batchingTable.startBatchingAt
   3 5

Есть также значение batchSize, которое следует установить в 5. По умолчанию, значение этого атрибута - 50:

Переключить отображение номеров строк
   1 >>> batchingTable.batchSize
   2 50
   3 >>> batchingTable.batchSize = 5
   4 >>> batchingTable.batchSize
   5 5

Теперь мы можем обновить и отобразить таблицу снова. Вы увидите только 5 строк таблицы, и это правильно. Порядок не зависит от чисел, которы мы видим в ячейках:

Переключить отображение номеров строк
   1 >>> batchingTable.update()
   2 >>> print batchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Eighteenth item</td>
  11       <td>number: 18</td>
  12     </tr>
  13     <tr>
  14       <td>Eighth item</td>
  15       <td>number: 8</td>
  16     </tr>
  17     <tr>
  18       <td>Eleventh item</td>
  19       <td>number: 11</td>
  20     </tr>
  21     <tr>
  22       <td>Fifteenth item</td>
  23       <td>number: 15</td>
  24     </tr>
  25     <tr>
  26       <td>First item</td>
  27       <td>number: 1</td>
  28     </tr>
  29   </tbody>
  30 </table>

Я думаю нам следует сортировать таблицу по второму столбцу перед тем, как показывать следующие порции данных. Делаем мы это просто установив defaultSortOn:

Переключить отображение номеров строк
   1 >>> batchingTable.sortOn = u'table-number-1'

Теперь мы должны увидеть хорошо отсортированную таблицу:

Переключить отображение номеров строк
   1 >>> batchingTable.update()
   2 >>> print batchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Zero item</td>
  11       <td>number: 0</td>
  12     </tr>
  13     <tr>
  14       <td>First item</td>
  15       <td>number: 1</td>
  16     </tr>
  17     <tr>
  18       <td>Second item</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Third item</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>Fourth item</td>
  27       <td>number: 4</td>
  28     </tr>
  29   </tbody>
  30 </table>

Концепция порций позволяет нам выбрать со всех порций только тот набор данных, который нам нужен. Сделать это мы можем установив любую порцию как строки. Как видите, у нас есть 4 строковые порции данных:

Переключить отображение номеров строк
   1 >>> len(batchingTable.rows.batches)
   2 4

Мы можем устанавливать такие порции как строки, тогда данные этот порции будут использоваться при отображении. Но будьте внимательны, так как если мы обновим таблицу, наши строки перекроют и обнулят предыдущие значения. Это значит, что вы можете установить любую порцию как строковые данные и отобразить только их. Это доступно, так как метод update отсортировал все элементы и порцию, которая содержит данные готовые для использования. Эта концепция очень важна, если вам нужно кешировать порции данных.

Переключить отображение номеров строк
   1 >>> batchingTable.rows = batchingTable.rows.batches[1]
   2 >>> print batchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Sixth item</td>
  11       <td>number: 6</td>
  12     </tr>
  13     <tr>
  14       <td>Seventh item</td>
  15       <td>number: 7</td>
  16     </tr>
  17     <tr>
  18       <td>Eighth item</td>
  19       <td>number: 8</td>
  20     </tr>
  21     <tr>
  22       <td>Ninth item</td>
  23       <td>number: 9</td>
  24     </tr>
  25     <tr>
  26       <td>Tenth item</td>
  27       <td>number: 10</td>
  28     </tr>
  29   </tbody>
  30 </table>

Как говорилось выше, если вы вызываете метод update наши настройки порций строк обнуляются:

Переключить отображение номеров строк
   1 >>> batchingTable.update()
   2 >>> print batchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Zero item</td>
  11       <td>number: 0</td>
  12     </tr>
  13     <tr>
  14       <td>First item</td>
  15       <td>number: 1</td>
  16     </tr>
  17     <tr>
  18       <td>Second item</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Third item</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>Fourth item</td>
  27       <td>number: 4</td>
  28     </tr>
  29   </tbody>
  30 </table>

Это значит, что, возможно, вы можете обновить все порции, закешировать их, и использовать позже, но, обычно, это не очень полезно, так как нужно проектировать страницу специально для таких нужд. Это также значит, что должен быть еще один путь установить индекс порций данных. И он существует. Есть два других способа, с помощью которых можно установить положение порции. Мы можем установить положение порции с помощью значения batchStart в таблице, или использовать переменную запроса. Давайте сначала посмотрим первый вариант:

Переключить отображение номеров строк
   1 >>> batchingTable.batchStart = 6
   2 >>> batchingTable.update()
   3 >>> print batchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Seventh item</td>
  11       <td>number: 7</td>
  12     </tr>
  13     <tr>
  14       <td>Eighth item</td>
  15       <td>number: 8</td>
  16     </tr>
  17     <tr>
  18       <td>Ninth item</td>
  19       <td>number: 9</td>
  20     </tr>
  21     <tr>
  22       <td>Tenth item</td>
  23       <td>number: 10</td>
  24     </tr>
  25     <tr>
  26       <td>Eleventh item</td>
  27       <td>number: 11</td>
  28     </tr>
  29   </tbody>
  30 </table>

Мы также можем установить положение порции используя значение batchStart из запроса. Заметьте, что нам нужен префикс таблицы и _ _name_ _ колонки, как было в случае с сортировкой:

Переключить отображение номеров строк
   1 >>> batchingRequest = TestRequest(form={'table-batchStart': '11',
   2 ...                                     'table-batchSize': '5',
   3 ...                                     'table-sortOn': 'table-number-1'})
   4 >>> requestBatchingTable = BatchingTable(folder, batchingRequest)

Нам также потребуется предоставить таблице положение (location) и имя, как обычно делается с траверсингом:

Переключить отображение номеров строк
   1 >>> requestBatchingTable.__parent__ = folder
   2 >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'

Наша таблица должна начать делать порции по меньшему количеству элементов, нежели у нас есть по умолчанию, в противном случае мы не получим порции.

Переключить отображение номеров строк
   1 >>> requestBatchingTable.startBatchingAt = 5
   2 >>> requestBatchingTable.update()
   3 >>> print requestBatchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Twelfth item</td>
  11       <td>number: 12</td>
  12     </tr>
  13     <tr>
  14       <td>Thirteenth item</td>
  15       <td>number: 13</td>
  16     </tr>
  17     <tr>
  18       <td>Fourteenth item</td>
  19       <td>number: 14</td>
  20     </tr>
  21     <tr>
  22       <td>Fifteenth item</td>
  23       <td>number: 15</td>
  24     </tr>
  25     <tr>
  26       <td>Sixteenth item</td>
  27       <td>number: 16</td>
  28     </tr>
  29   </tbody>
  30 </table>

BatchProvider

Провайдер порций позволяет отображать порцию HTML независимо от таблицы. Это значит, что по умолчанию порция не отображается методом render. Вы можете поменять такое поведение в своей реализации таблицы и возвращать порцию и таблицу в методе render.

Как видите, строки нашей таблицы предоставляют IBatch:

Переключить отображение номеров строк
   1 >>> from z3c.batching.interfaces import IBatch
   2 >>> IBatch.providedBy(requestBatchingTable.rows)
   3 True

Давайте проверим переменные порций перед тем, как отобразить наши тестовые данные. Это позволяет нам сравнивать результаты отображения. Для получения более подробной информации о порциях обратитесь к README.txt в z3c.batching:

Переключить отображение номеров строк
   1 >>> requestBatchingTable.rows.start
   2 11
   3 >>> requestBatchingTable.rows.index
   4 2
   5 >>> requestBatchingTable.rows.batches
   6 <z3c.batching.batch.Batches object at ...>
   7 >>> len(requestBatchingTable.rows.batches)
   8 4

Мы используем предыдущую таблицу и отображаем порцию с помощью встроенного метода renderBatch:

Переключить отображение номеров строк
   1 >>> requestBatchingTable.update()
   2 >>> print requestBatchingTable.renderBatch()

Переключить отображение номеров строк
   1 <a href="...html?table-batchStart=0&table-batchSize=5" class="first">1</a>
   2 <a href="...html?table-batchStart=5&table-batchSize=5">2</a>
   3 <a href="...html?table-batchStart=11&table-batchSize=5" class="current">3</a>
   4 <a href="...html?table-batchStart=15&table-batchSize=5" class="last">4</a>

Давайте теперь добавим еще элементов, чтобы протестировать пропущенные ссылки в больших порциях:

Переключить отображение номеров строк
   1 >>> for i in range(1000):
   2 ...     idx = i+20
   3 ...     folder[str(idx)] = File(str(idx), idx)

Теперь протестируем таблицу снова с новым набором элементов и значением startBatchingAt - 5, но начиная порцию с элемента с позицией 100 и отсортированной по втором столбце:

Переключить отображение номеров строк
   1 >>> batchingRequest = TestRequest(form={'table-batchStart': '100',
   2 ...                                     'table-batchSize': '5',
   3 ...                                     'table-sortOn': 'table-number-1'})
   4 >>> requestBatchingTable = BatchingTable(folder, batchingRequest)
   5 >>> requestBatchingTable.startBatchingAt = 5

Нам также следует дать таблице положение (location) и имя, как обычно делается при траверсинге:

Переключить отображение номеров строк
   1 >>> requestBatchingTable.__parent__ = folder
   2 >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'
   3 >>> requestBatchingTable.update()
   4 >>> print requestBatchingTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>100 item</td>
  11       <td>number: 100</td>
  12     </tr>
  13     <tr>
  14       <td>101 item</td>
  15       <td>number: 101</td>
  16     </tr>
  17     <tr>
  18       <td>102 item</td>
  19       <td>number: 102</td>
  20     </tr>
  21     <tr>
  22       <td>103 item</td>
  23       <td>number: 103</td>
  24     </tr>
  25     <tr>
  26       <td>104 item</td>
  27       <td>number: 104</td>
  28     </tr>
  29   </tbody>
  30 </table>

И тестируем порцию. Заметьте, что три точки между ссылками отображаются провайдером порций и не являются частью доктеста:

{{{#highlight python >>> print requestBatchingTable.renderBatch() }}}

Переключить отображение номеров строк
   1 <a href="...html?table-batchStart=0&table-batchSize=5" class="first">1</a>
   2 ...
   3 <a href="...html?table-batchStart=85&table-batchSize=5">18</a>
   4 <a href="...html?table-batchStart=90&table-batchSize=5">19</a>
   5 <a href="...html?table-batchStart=95&table-batchSize=5">20</a>
   6 <a href="...html?table-batchStart=100&table-batchSize=5" class="current">21</a>
   7 <a href="...html?table-batchStart=105&table-batchSize=5">22</a>
   8 <a href="...html?table-batchStart=110&table-batchSize=5">23</a>
   9 <a href="...html?table-batchStart=115&table-batchSize=5">24</a>
  10 ...
  11 <a href="...html?table-batchStart=1015&table-batchSize=5" class="last">204</a>

Вы можете изменить разделитель в провайдере порций, установив значение batchSpacer:

Переключить отображение номеров строк
   1 >>> from z3c.table.batch import BatchProvider
   2 >>> class XBatchProvider(BatchProvider):
   3 ...     """Just another batch provider."""
   4 ...     batchSpacer = u'xxx'

Теперь зарегистрируйте новый провайдер порций:

Переключить отображение номеров строк
   1 >>> import zope.publisher.interfaces.browser
   2 >>> zope.component.provideAdapter(XBatchProvider,
   3 ...     (zope.interface.Interface,
   4 ...      zope.publisher.interfaces.browser.IBrowserRequest,
   5 ...      BatchingTable), name='batch')

Если мы обновим и отобразим таблицу, будет использован новый провайдер порций. Как видите, разделитель изменился:

Переключить отображение номеров строк
   1 >>> requestBatchingTable.update()
   2 >>> print requestBatchingTable.renderBatch()

Переключить отображение номеров строк
   1 <a href="...html?table-batchStart=0&table-batchSize=5" class="first">1</a>
   2 xxx
   3 <a href="...html?table-batchStart=85&table-batchSize=5">18</a>
   4 <a href="...html?table-batchStart=90&table-batchSize=5">19</a>
   5 <a href="...html?table-batchStart=95&table-batchSize=5">20</a>
   6 <a href="...html?table-batchStart=100&table-batchSize=5" class="current">21</a>
   7 <a href="...html?table-batchStart=105&table-batchSize=5">22</a>
   8 <a href="...html?table-batchStart=110&table-batchSize=5">23</a>
   9 <a href="...html?table-batchStart=115&table-batchSize=5">24</a>
  10 xxx
  11 <a href="...html?table-batchStart=1015&table-batchSize=5" class="last">204</a>

Возможно, это не имеет смысла, но давайте посмотрит, что происходит, если мы установим предыдущий и следующий размер порции в 0 (ноль):

Переключить отображение номеров строк
   1 >>> from z3c.table.batch import BatchProvider
   2 >>> class ZeroBatchProvider(BatchProvider):
   3 ...     """Just another batch provider."""
   4 ...     batchSpacer = u'xxx'
   5 ...     previousBatchSize = 0
   6 ...     nextBatchSize = 0

Теперь зарегистрируем новый провайдер порций:

Переключить отображение номеров строк
   1 >>> import zope.publisher.interfaces.browser
   2 >>> zope.component.provideAdapter(ZeroBatchProvider,
   3 ...     (zope.interface.Interface,
   4 ...      zope.publisher.interfaces.browser.IBrowserRequest,
   5 ...      BatchingTable), name='batch')

Обновите таблицу и отобразите порцию:

Переключить отображение номеров строк
   1 >>> requestBatchingTable.update()
   2 >>> print requestBatchingTable.renderBatch()

Переключить отображение номеров строк
   1 <a href="...html?table-batchStart=0&table-batchSize=5" class="first">1</a>
   2 xxx
   3 <a href="...html?table-batchStart=100&table-batchSize=5" class="current">21</a>
   4 xxx
   5 <a href="...html?table-batchStart=1015&table-batchSize=5" class="last">204</a>

SequenceTable

Таблица последовательности может быть использована, если нужно предоставить таблицу для последовательности элементов, вместо отображения (mapping). Определите такую же последовательность элементов, как мы использовали до того, как добавили 1000 элементов:

Переключить отображение номеров строк
   1 >>> dataSequence = []
   2 >>> dataSequence.append(File('Zero', 0))
   3 >>> dataSequence.append(File('First', 1))
   4 >>> dataSequence.append(File('Second', 2))
   5 >>> dataSequence.append(File('Third', 3))
   6 >>> dataSequence.append(File('Fourth', 4))
   7 >>> dataSequence.append(File('Fifth', 5))
   8 >>> dataSequence.append(File('Sixth', 6))
   9 >>> dataSequence.append(File('Seventh', 7))
  10 >>> dataSequence.append(File('Eighth', 8))
  11 >>> dataSequence.append(File('Ninth', 9))
  12 >>> dataSequence.append(File('Tenth', 10))
  13 >>> dataSequence.append(File('Eleventh', 11))
  14 >>> dataSequence.append(File('Twelfth', 12))
  15 >>> dataSequence.append(File('Thirteenth', 13))
  16 >>> dataSequence.append(File('Fourteenth', 14))
  17 >>> dataSequence.append(File('Fifteenth', 15))
  18 >>> dataSequence.append(File('Sixteenth', 16))
  19 >>> dataSequence.append(File('Seventeenth', 17))
  20 >>> dataSequence.append(File('Eighteenth', 18))
  21 >>> dataSequence.append(File('Nineteenth', 19))
  22 >>> dataSequence.append(File('Twentieth', 20))

Теперь давайте определим новую SequenceTable:

Переключить отображение номеров строк
   1 >>> class SequenceTable(table.SequenceTable):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         return [
   5 ...             column.addColumn(self, TitleColumn, u'title',
   6 ...                              cellRenderer=cellRenderer,
   7 ...                              headCellRenderer=headCellRenderer,
   8 ...                              weight=1),
   9 ...             column.addColumn(self, NumberColumn, name=u'number',
  10 ...                              weight=2, header=u'Number')
  11 ...             ]

Теперь мы можем создать таблицу, адаптируя последовательность:

Переключить отображение номеров строк
   1 >>> sequenceRequest = TestRequest(form={'table-batchStart': '0',
   2 ...                                     'table-sortOn': 'table-number-1'})
   3 >>> sequenceTable = SequenceTable(dataSequence, sequenceRequest)

Нам также потребуется дать имя и положение таблице:

Переключить отображение номеров строк
   1 >>> sequenceTable.__parent__ = folder
   2 >>> sequenceTable.__name__ = u'sequenceTable.html'

Обновите и отобразите таблицу последовательности:

Переключить отображение номеров строк
   1 >>> sequenceTable.update()
   2 >>> print sequenceTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Zero item</td>
  11       <td>number: 0</td>
  12     </tr>
  13     <tr>
  14       <td>First item</td>
  15       <td>number: 1</td>
  16     </tr>
  17     <tr>
  18       <td>Second item</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Third item</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>Fourth item</td>
  27       <td>number: 4</td>
  28     </tr>
  29     <tr>
  30       <td>Fifth item</td>
  31       <td>number: 5</td>
  32     </tr>
  33     <tr>
  34       <td>Sixth item</td>
  35       <td>number: 6</td>
  36     </tr>
  37     <tr>
  38       <td>Seventh item</td>
  39       <td>number: 7</td>
  40     </tr>
  41     <tr>
  42       <td>Eighth item</td>
  43       <td>number: 8</td>
  44     </tr>
  45     <tr>
  46       <td>Ninth item</td>
  47       <td>number: 9</td>
  48     </tr>
  49     <tr>
  50       <td>Tenth item</td>
  51       <td>number: 10</td>
  52     </tr>
  53     <tr>
  54       <td>Eleventh item</td>
  55       <td>number: 11</td>
  56     </tr>
  57     <tr>
  58       <td>Twelfth item</td>
  59       <td>number: 12</td>
  60     </tr>
  61     <tr>
  62       <td>Thirteenth item</td>
  63       <td>number: 13</td>
  64     </tr>
  65     <tr>
  66       <td>Fourteenth item</td>
  67       <td>number: 14</td>
  68     </tr>
  69     <tr>
  70       <td>Fifteenth item</td>
  71       <td>number: 15</td>
  72     </tr>
  73     <tr>
  74       <td>Sixteenth item</td>
  75       <td>number: 16</td>
  76     </tr>
  77     <tr>
  78       <td>Seventeenth item</td>
  79       <td>number: 17</td>
  80     </tr>
  81     <tr>
  82       <td>Eighteenth item</td>
  83       <td>number: 18</td>
  84     </tr>
  85     <tr>
  86       <td>Nineteenth item</td>
  87       <td>number: 19</td>
  88     </tr>
  89     <tr>
  90       <td>Twentieth item</td>
  91       <td>number: 20</td>
  92     </tr>
  93   </tbody>
  94 </table>

Как видите, элементы отобразились на базе последовательности данных. Теперь установим начальный размер порций в 5:

Переключить отображение номеров строк
   1 >>> sequenceTable.startBatchingAt = 5

И размер порций в 5.

Переключить отображение номеров строк
   1 >>> sequenceTable.batchSize = 5

Теперь мы можем обновить и отобразить таблицу снова. Но вы увидите, что мы получили таблицу только из 5 строк:

Переключить отображение номеров строк
   1 >>> sequenceTable.update()
   2 >>> print sequenceTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>My items</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>Zero item</td>
  11       <td>number: 0</td>
  12     </tr>
  13     <tr>
  14       <td>First item</td>
  15       <td>number: 1</td>
  16     </tr>
  17     <tr>
  18       <td>Second item</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>Third item</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>Fourth item</td>
  27       <td>number: 4</td>
  28     </tr>
  29   </tbody>
  30 </table>

И мы установили порядок сортировки в инверсный даже при использовании порций:

Переключить отображение номеров строк
   1 >>> sequenceTable.sortOrder = u'reverse'
   2 >>> sequenceTable.update()
   3 >>> print sequenceTable.render()

<table>
  <thead>
    <tr>
      <th>My items</th>
      <th>Number</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Twentieth item</td>
      <td>number: 20</td>
    </tr>
    <tr>
      <td>Nineteenth item</td>
      <td>number: 19</td>
    </tr>
    <tr>
      <td>Eighteenth item</td>
      <td>number: 18</td>
    </tr>
    <tr>
      <td>Seventeenth item</td>
      <td>number: 17</td>
    </tr>
    <tr>
      <td>Sixteenth item</td>
      <td>number: 16</td>
    </tr>
  </tbody>
</table>

Заголовки

Мы можем изменить отображение заголовка , например, столбца Title, зарегистрировав адаптер к IHeaderColumn. Это может быть полезно для добавления ссылок на заголовки столбцов существующих реализаций таблицы.

Давайте использовать свежую, почти пустую папку:

>>> folder = Folder()
>>> root['folder-1'] = folder
>>> folder[u'first'] = File('First', 1)
>>> folder[u'second'] = File('Second', 2)
>>> folder[u'third'] = File('Third', 3)
>>> class myTableClass(table.Table):
...     pass
>>> myTable = myTableClass(folder, request)
>>> class TitleColumn(column.Column):
...
...     header = u'Title'
...
...     def renderCell(self, item):
...         return item.title

Теперь мы может зарегистрировать адаптер столбца прямо на класс таблицы.

Переключить отображение номеров строк
   1 >>> zope.component.provideAdapter(TitleColumn,
   2 ...     (None, None, myTableClass), provides=interfaces.IColumn,
   3 ...      name='titleColumn')
   4 }}
   5 
   6 И добавляем регистрацию заголовка столбца - будем использовать предоставленную базовую реализацию сортировки.
   7 
   8 {{{#!highlight python
   9 >>> from z3c.table.header import SortingColumnHeader
  10 >>> zope.component.provideAdapter(SortingColumnHeader,
  11 ...     (None, None, interfaces.ITable, interfaces.IColumn),
  12 ...     provides=interfaces.IColumnHeader)

Теперь мы можем отобразить таблицу и увидим ссылку в заголовке. Заметьте, что он предназначен для переключения направления сортировки, так как таблица изначально будет отображать первую колонку по возрастанию.

Переключить отображение номеров строк
   1 >>> myTable.update()
   2 >>> print myTable.render()

Переключить отображение номеров строк
   1 <table>
   2  <thead>
   3   <tr>
   4    <th><a
   5     href="?table-sortOrder=descending&table-sortOn=table-titleColumn-0"
   6     title="Sort">Title</a></th>
   7 ...
   8 </table>

Другое

Давайте сделаем обзор веселым и протестируем разные вещи.

Протестируем, возвращает ли метод getWeight 0 (ноль) при AttributeError:

Переключить отображение номеров строк
   1 >>> from z3c.table.table import getWeight
   2 >>> getWeight(None)
   3 0

Попробуйте вызвать простую таблицу и renderBatch, который должен возвратить пустую строку:

Переключить отображение номеров строк
   1 >>> simpleTable = table.Table(folder, request)
   2 >>> simpleTable.renderBatch()
   3 u''

Попробуйте отобразить пустую таблицу, которая адаптируется к пустому отображению (mapping):

Переключить отображение номеров строк
   1 >>> simpleTable = table.Table({}, request)
   2 >>> simpleTable.render()
   3 u''

Давайте посмотрит, вызывает ли addColumn ValueError, если нет класса Column:

Переключить отображение номеров строк
   1 >>> column.addColumn(simpleTable, column.Column, u'dummy')
   2 <Column u'dummy'>
   3 >>> column.addColumn(simpleTable, None, u'dummy')
   4 ...
   5 ValueError: class_ None must implement IColumn.

Протестируем, можно ли передавать дополнительные аргументы в addColumn:

Переключить отображение номеров строк
   1 >>> simpleColumn = column.addColumn(simpleTable, column.Column, u'dummy',
   2 ...     foo='foo value', bar=u'something else', counter=99)
   3 >>> simpleColumn.foo
   4 'foo value'
   5 >>> simpleColumn.bar
   6 u'something else'
   7 >>> simpleColumn.counter
   8 99

Класс NoneCell предоставляет методы, которые никогда не используются. Но эти методы должны быть так, так как они определены в интерфейсе. Давайте протестируем значения по умолчанию.

Давайте сначала возьмем объект из папки:

Переключить отображение номеров строк
   1 >>> firstItem = folder[u'first']
   2 >>> noneCellColumn = column.addColumn(simpleTable, column.NoneCell, u'none')
   3 >>> noneCellColumn.renderCell(firstItem)
   4 u''
   5 >>> noneCellColumn.getColspan(firstItem)
   6 0
   7 >>> noneCellColumn.renderHeadCell()
   8 u''
   9 >>> noneCellColumn.renderCell(firstItem)
  10 u''

Реализация столбца (Column) по умолчанию вызывает NotImplementedError, если мы не переопределили метод renderCell:

Переключить отображение номеров строк
   1 >>> defaultColumn = column.addColumn(simpleTable, column.Column, u'default')
   2 >>> defaultColumn.renderCell(firstItem)
   3 ...
   4 NotImplementedError: Subclass must implement renderCell

Column (Столбец)

Давайте покажем разницу между столбцами, которые мы предлагаем по умолчанию. Но сначала взгляните на README.txt, который объясняет концепции Table (таблицы) и Column (колонки).

Настройка тестовых данных

Давайте создадим тестовый контейнер, который сможем использовать как итерируемый контекст:

Переключить отображение номеров строк
   1 >>> from zope.app.container import btree
   2 >>> class Container(btree.BTreeContainer):
   3 ...     """Sample container."""
   4 >>> container = Container()
   5 >>> root['container'] = container

и создадим тестовый контент объект, который будем использовать как содержимое контейнера:

Переключить отображение номеров строк
   1 >>> class Content(object):
   2 ...     """Sample content."""
   3 ...     def __init__(self, title, number):
   4 ...         self.title = title
   5 ...         self.number = number

Теперь добавим несколько объектов в контейнер:

Переключить отображение номеров строк
   1 >>> container[u'zero'] = Content('Zero', 0)
   2 >>> container[u'first'] = Content('First', 1)
   3 >>> container[u'second'] = Content('Second', 2)
   4 >>> container[u'third'] = Content('Third', 3)
   5 >>> container[u'fourth'] = Content('Fourth', 4)

Давайте также создадим простую сортируемую колонку:

Переключить отображение номеров строк
   1 >>> from z3c.table import column
   2 >>> class NumberColumn(column.Column):
   3 ...
   4 ...     header = u'Number'
   5 ...     weight = 20
   6 ...
   7 ...     def getSortKey(self, item):
   8 ...         return item.number
   9 ...
  10 ...     def renderCell(self, item):
  11 ...         return 'number: %s' % item.number

NameColumn

Давайте определим таблицу с использованием NameColumn:

Подсветка не поддерживается для синтаксиса «python:», см. список поддерживаемых синтаксисов для подсветки на странице ПомощьПоПарсерам.
Переключить отображение номеров строк
   1 >>> from z3c.table import table
   2 >>> class NameTable(table.Table):
   3 ...
   4 ...     def setUpColumns(self):
   5 ...         return [
   6 ...             column.addColumn(self, column.NameColumn, u'name',
   7 ...                              weight=1),
   8 ...             column.addColumn(self, NumberColumn, name=u'number',
   9 ...                              weight=2, header=u'Number')
  10 ...             ]

Теперь создадим, обновим и отобразим таблицу и вы увидите, что NameColumn отображает имя объекта, используя метод zope.traversing.api.getName():

Переключить отображение номеров строк
   1 >>> from zope.publisher.browser import TestRequest
   2 >>> request = TestRequest()
   3 >>> nameTable = NameTable(container, request)
   4 >>> nameTable.update()
   5 >>> print nameTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Name</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td>first</td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td>fourth</td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td>second</td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td>third</td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td>zero</td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

RadioColumn

Давайте определим таблицу используя RadioColumn:

Переключить отображение номеров строк
   1 >>> class RadioTable(table.Table):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         return [
   5 ...             column.addColumn(self, column.RadioColumn, u'radioColumn',
   6 ...                              weight=1),
   7 ...             column.addColumn(self, NumberColumn, name=u'number',
   8 ...                              weight=2, header=u'Number')
   9 ...             ]

Теперь создадим, обновим и отобразим таблицу:

Переключить отображение номеров строк
   1 >>> request = TestRequest()
   2 >>> radioTable = RadioTable(container, request)
   3 >>> radioTable.update()
   4 >>> print radioTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>X</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="first"  /></td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="fourth"  /></td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="second"  /></td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="third"  /></td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="zero"  /></td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

Как видите, мы можем насильно отобразить поле опционного ввода, как выбрано в значении, что передается через переменную запроса:

Переключить отображение номеров строк
   1 >>> radioRequest = TestRequest(form={'table-radioColumn-0-selectedItem': 'third'})
   2 >>> radioTable = RadioTable(container, radioRequest)
   3 >>> radioTable.update()
   4 >>> print radioTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>X</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="first"  /></td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="fourth"  /></td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="second"  /></td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="third" checked="checked" /></td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="zero"  /></td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

CheckBoxColumn

Давайте определим таблицу с использованием CheckBoxColumn:

Переключить отображение номеров строк
   1 >>> class CheckBoxTable(table.Table):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         return [
   5 ...             column.addColumn(self, column.CheckBoxColumn, u'checkBoxColumn',
   6 ...                              weight=1),
   7 ...             column.addColumn(self, NumberColumn, name=u'number',
   8 ...                              weight=2, header=u'Number')
   9 ...             ]

Теперь создадим, обновим и отобразим таблицу:

Переключить отображение номеров строк
   1 >>> request = TestRequest()
   2 >>> checkBoxTable = CheckBoxTable(container, request)
   3 >>> checkBoxTable.update()
   4 >>> print checkBoxTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>X</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first"  /></td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second"  /></td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third"  /></td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero"  /></td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

И снова вы можете насильно отобразить поле ввода с галочкой, как указано в переменной запроса:

Переключить отображение номеров строк
   1 >>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
   2 ...                                     ['first', 'third']})
   3 >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
   4 >>> checkBoxTable.update()
   5 >>> print checkBoxTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>X</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr>
  10       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second"  /></td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr>
  22       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero"  /></td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

Если вы выбираете строку, вы также можете предоставить дополнительный CSS стиль. Это может быть использовано при создании перемежающихся строк:

Переключить отображение номеров строк
   1 >>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
   2 ...                                     ['first', 'third']})
   3 >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
   4 >>> checkBoxTable.cssClasses = {'tr': 'tr'}
   5 >>> checkBoxTable.cssClassSelected = u'selected'
   6 >>> checkBoxTable.cssClassEven = u'even'
   7 >>> checkBoxTable.cssClassOdd = u'odd'
   8 >>> checkBoxTable.update()
   9 >>> print checkBoxTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr class="tr">
   4       <th>X</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr class="selected even tr">
  10       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr class="odd tr">
  14       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr class="even tr">
  18       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second"  /></td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr class="selected odd tr">
  22       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr class="even tr">
  26       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero"  /></td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

Давайте протестируем cssClassSelected без других css классов

Переключить отображение номеров строк
   1 >>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems':
   2 ...                                     ['first', 'third']})
   3 >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest)
   4 >>> checkBoxTable.cssClassSelected = u'selected'
   5 >>> checkBoxTable.update()
   6 >>> print checkBoxTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>X</th>
   5       <th>Number</th>
   6     </tr>
   7   </thead>
   8   <tbody>
   9     <tr class="selected">
  10       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td>
  11       <td>number: 1</td>
  12     </tr>
  13     <tr>
  14       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth"  /></td>
  15       <td>number: 4</td>
  16     </tr>
  17     <tr>
  18       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second"  /></td>
  19       <td>number: 2</td>
  20     </tr>
  21     <tr class="selected">
  22       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td>
  23       <td>number: 3</td>
  24     </tr>
  25     <tr>
  26       <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero"  /></td>
  27       <td>number: 0</td>
  28     </tr>
  29   </tbody>
  30 </table>

CreatedColumn

Давайте определим таблицу с использованием CreatedColumn:

Переключить отображение номеров строк
   1 >>> class CreatedColumnTable(table.Table):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         return [
   5 ...             column.addColumn(self, column.CreatedColumn, u'createdColumn',
   6 ...                              weight=1),
   7 ...             ]

Теперь создадим, обновим и отобразим таблицу. Заметьте, что мы используем тестовый адаптер к dublin core, который возвращает 01/01/01 01:01 в качестве даты создания:

Переключить отображение номеров строк
   1 >>> request = TestRequest()
   2 >>> createdColumnTable = CreatedColumnTable(container, request)
   3 >>> createdColumnTable.update()
   4 >>> print createdColumnTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Created</th>
   5     </tr>
   6   </thead>
   7   <tbody>
   8     <tr>
   9       <td>01/01/01 01:01</td>
  10     </tr>
  11     <tr>
  12       <td>01/01/01 01:01</td>
  13     </tr>
  14     <tr>
  15       <td>01/01/01 01:01</td>
  16     </tr>
  17     <tr>
  18       <td>01/01/01 01:01</td>
  19     </tr>
  20     <tr>
  21       <td>01/01/01 01:01</td>
  22     </tr>
  23   </tbody>
  24 </table>

ModifiedColumn

Давайте определим таблицу используя ModifiedColumn:

Переключить отображение номеров строк
   1 >>> class ModifiedColumnTable(table.Table):
   2 ...
   3 ...     def setUpColumns(self):
   4 ...         return [
   5 ...             column.addColumn(self, column.ModifiedColumn,
   6 ...                              u'modifiedColumn', weight=1),
   7 ...             ]

Теперь создадим, обновим и отобразим таблицу. Заметьте, мы использовали тестовый адаптер dublin core, который возвращает 02/02/02 02:02 в качестве даты модификации:

Переключить отображение номеров строк
   1 >>> request = TestRequest()
   2 >>> modifiedColumnTable = ModifiedColumnTable(container, request)
   3 >>> modifiedColumnTable.update()
   4 >>> print modifiedColumnTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th>Modified</th>
   5     </tr>
   6   </thead>
   7   <tbody>
   8     <tr>
   9       <td>02/02/02 02:02</td>
  10     </tr>
  11     <tr>
  12       <td>02/02/02 02:02</td>
  13     </tr>
  14     <tr>
  15       <td>02/02/02 02:02</td>
  16     </tr>
  17     <tr>
  18       <td>02/02/02 02:02</td>
  19     </tr>
  20     <tr>
  21       <td>02/02/02 02:02</td>
  22     </tr>
  23   </tbody>
  24 </table>

GetAttrColumn

Столбец GetAttrColumn - это помесь (mixin), которая используется в CreatedColumn и в ModifiedColumn. Если все хорошо, то используется не весь код. Давайте протестируем эту колонку саму по себе:

Переключить отображение номеров строк
   1 >>> class GetTitleColumn(column.GetAttrColumn):
   2 ...
   3 ...     attrName = 'title'
   4 ...     defaultValue = u'missing'
   5 >>> class GetAttrColumnTable(table.Table):
   6 ...
   7 ...     attrName = 'title'
   8 ...     defaultValue = u'missing'
   9 ...
  10 ...     def setUpColumns(self):
  11 ...         return [
  12 ...             column.addColumn(self, GetTitleColumn, u'title'),
  13 ...             ]

Обновите и отобразите таблицу:

Переключить отображение номеров строк
   1 >>> request = TestRequest()
   2 >>> getAttrColumnTable = GetAttrColumnTable(container, request)
   3 >>> getAttrColumnTable.update()
   4 >>> print getAttrColumnTable.render()

Переключить отображение номеров строк
   1 <table>
   2   <thead>
   3     <tr>
   4       <th></th>
   5     </tr>
   6   </thead>
   7   <tbody>
   8     <tr>
   9       <td>First</td>
  10     </tr>
  11     <tr>
  12       <td>Fourth</td>
  13     </tr>
  14     <tr>
  15       <td>Second</td>
  16     </tr>
  17     <tr>
  18       <td>Third</td>
  19     </tr>
  20     <tr>
  21       <td>Zero</td>
  22     </tr>
  23   </tbody>
  24 </table>

Если мы используем не существующий атрибут, то не получаете AttributeError, а значение по умолчанию, которое определено в GetAttrColumnTable.

Переключить отображение номеров строк
   1 >>> class UndefinedAttributeColumn(column.GetAttrColumn):
   2 ...
   3 ...     attrName = 'undefined'
   4 ...     defaultValue = u'missing'
   5 >>> class GetAttrColumnTable(table.Table):
   6 ...
   7 ...     attrName = 'title'
   8 ...     defaultValue = u'missing'
   9 ...
  10 ...     def setUpColumns(self):
  11 ...         return [
  12 ...             column.addColumn(self, UndefinedAttributeColumn, u'missing'),
  13 ...             ]

Обновите и отобразите таблицу:

Переключить отображение номеров строк
   1 >>> request = TestRequest()
   2 >>> getAttrColumnTable = GetAttrColumnTable(container, request)
   3 >>> getAttrColumnTable.update()
   4 >>> 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>

Несуществующий attrName в GetAttrColumn также вернет defaultValue:

Переключить отображение номеров строк
   1 >>> class BadAttributeColumn(column.GetAttrColumn):
   2 ...
   3 ...     defaultValue = u'missing'
   4 >>> firstItem = container[u'first']
   5 >>> simpleTable = table.Table(container, request)
   6 >>> badColumn = column.addColumn(simpleTable, BadAttributeColumn, u'bad')
   7 >>> badColumn.renderCell(firstItem)
   8 u'missing'

Если мы попытаемся получить доступ к защищенному атрибуту, объект вызовет исключение Unauthorized. В этом случае вы также получите defaultValue. Давайте установим объект, который вызывает такую ошибку при попытке доступа к атрибуту title:

Переключить отображение номеров строк
   1 >>> from zope.security.interfaces import Unauthorized
   2 >>> class ProtectedItem(object):
   3 ...
   4 ...     @property
   5 ...     def forbidden(self):
   6 ...         raise Unauthorized, 'forbidden'

Установите и протестируйте объект:

Переключить отображение номеров строк
   1 >>> protectedItem = ProtectedItem()
   2 >>> protectedItem.forbidden
   3 ...
   4 Unauthorized: forbidden

Теперь определим столбец:

Переключить отображение номеров строк
   1 >>> class ForbiddenAttributeColumn(column.GetAttrColumn):
   2 ...
   3 ...     attrName = 'forbidden'
   4 ...     defaultValue = u'missing'
   5 And test the attribute access:
   6 
   7 >>> simpleTable = table.Table(container, request)
   8 >>> badColumn = column.addColumn(simpleTable, ForbiddenAttributeColumn, u'x')
   9 >>> badColumn.renderCell(protectedItem)
  10 u'missing'

Перевод: Ростислав Дзинько

Пакеты/ZopePlone/Z3cTable (последним исправлял пользователь RostislavDzinko 2010-07-14 12:32:54)