'''z3c.table - продвинутые таблицы''' <> = Z3C Таблица = Цель, которую преследует пакет ''z3c.table'' - предложить модульную библиотеку для отрисовки таблиц. Мы используем шаблон "контент провайдер" с колонками, реализованными как адаптеры. Такой подход - мощная базовая концепция. == Важные требования == * разделение реализации в обновлении частей отрисовки. Такой подход позволяет манипулировать данными после обновления, и перед отрисовкой. * мы можем использовать шаблоны страниц, если потребуется, но, по умолчанию, все делается в Python. * мы может отрисовывать порционную навигацию отдельно от самой таблицы. == Никаких скинов == Этот пакет не предоставляет никаких шаблонов и скинов. В любом случае, когда вам нужно отрисовать красивую таблицу, вам придется писать свой собственный скин или шаблон. Отсутствие шаблонов и скинов позволяет удостоверится, что ''z3c.table'' имеет очень мало зависимостей, а поэтому легко поддается повторному использованию. == Заметка == Как вы, вероятно, уже знаете, перед тем, как выполняется сортировка по столбцам при порционном отображении данных таблицы, должна быть выполнена сортировка полного набора данных. При большом наборе данных это ведет к проблемам с быстродействием. При работе с большими объемами данных мы рекомендуем не совмещать порционное отображение с сортировкой по столбцам, либо, если нужно, предоставлять "умный" механизм кеширования данных для хранения отсортированных последовательностей. == Пример установки данных == Таблицы часто используются для отображения нормализованных порций данных. Например, нам необходимо отобразить информацию о файлах в папке. Каждый файл имеет заголовок, размер и тип. Наша таблица должна иметь строку для каждого файла и столбец для этих трех полей данных (заголовка, размера, и типа). Контекстом таблицы всегда является структура данных, по которой можно выполнить итерацию, при этом каждый элемент последовательности соответствует строке таблицы. Давайте создадим папку, которую мы сможем использовать как контекст: {{{#!highlight python >>> from zope.app.container import btree >>> class Folder(btree.BTreeContainer): ... """Sample folder.""" ... __name__ = u'folder' >>> folder = Folder() }}} XXX: не уверен, куда нам нужно положить эту папку. Также нам можно и не давать значение атрибуту '''_ _name_ _'''. Нам не нужно куда либо помещать ее. Давайте установим родительский элемент для папки: {{{#!highlight python >>> root['folder'] = folder }}} Теперь создадим простой объект '''File''' для наполнения созданной нами папки. {{{#!highlight python >>> class File(object): ... """Sample file.""" ... def __init__(self, title, size, type=None): ... self.title = title ... self.number = size ... self.type = type }}} Теперь давайте наполним созданную папку файлами. {{{#!highlight python >>> folder[u'first'] = File('First', 1) >>> folder[u'second'] = File('Second', 2) >>> folder[u'third'] = File('Third', 3) }}} == Создание таблиц == Теперь, когда у нас есть тестовые данные, с которыми можно работать, мы может создать таблицу. Так как таблицы - компоненты пользовательского интерфейса, они требуют и контекста и запроса (request). Они передаются как аргументы конструктору класса '''Table'''. {{{#!highlight python >>> from zope.publisher.browser import TestRequest >>> from z3c.table import table >>> request = TestRequest() >>> plainTable = table.Table(folder, request) }}} Когда таблица создана, мы можем ее обновлять и отрисовывать. Так как мы не указали столбцы таблицы, которые нужно отрисовать, таблица отрисуется как пустая строка: {{{#!highlight python >>> plainTable.update() >>> plainTable.render() u'' }}} Также стоит заметить, что класс '''Table''' - реализация интерфейса '''ITable'''. Намного интереснее взглянуть, что предоставляет '''ITable''', когда у нас есть несколько столбцов: {{{#!highlight python >>> from z3c.table import interfaces >>> from zope.interface.verify import verifyObject >>> verifyObject(interfaces.ITable, plainTable) True }}} == Создание столбцов == Так как нам может понадобится некий тип столбцов в многих разных таблицах, определение столбца лежит отдельно от самой таблицы. Каждый тип столбцов представлен собственным классом, который реализует интерфейс '''IColumn'''. Чтобы помочь в определении столбца, существует базовый класс '''Column'''. Простая колонка, которая отображает заголовок элемента выглядит примерно так: {{{#!highlight python >>> from z3c.table import column >>> class TitleColumn(column.Column): ... ... weight = 10 ... header = u'Title' ... ... def renderCell(self, item): ... return u'Title: %s' % item.title }}} Атрибут '''header''' - это текст, который мы можем увидеть в заголовке таблицы, обычно это тег ''''''. Атрибут '''weight''' указывает порядок колонки в таблице относительно других колонок. Метод '''renderCell''' делает всю работу по отображению, возвращая html структуру, которая будет находится внутри колонки таблица, обычно это тег ''''''. Метод '''renderCell''' должен предоставляться потомками класса '''Column'''. == Добавление колонки в таблицу с использованием адаптеров == Одним из способов добавления колонки в таблицу является адаптер. Хотя сначала такой подход кажется избыточным, он делает таблицы легко подключаемыми. Давайте зарегистрируем колонку как адаптер. {{{#!highlight python >>> import zope.component >>> zope.component.provideAdapter(TitleColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='firstColumn') }}} Теперь отрисуем таблицу еще раз: {{{#!highlight python >>> plainTable.update() >>> print plainTable.render() }}} {{{#!highlight html
Title
Title: First
Title: Second
Title: Third
}}} Мы также можем использовать предопределенное имя столбца: {{{#!highlight python >>> zope.component.provideAdapter(column.NameColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='secondColumn') }}} Теперь мы получим еще один дополнительный столбец: {{{#!highlight python >>> plainTable.update() >>> print plainTable.render() }}} {{{#!highlight html
Name Title
first Title: First
second Title: Second
third Title: Third
}}} == Объединение ячеек == Теперь давайте посмотрим, как можно сделать объединение ячеек для столбца: {{{#!highlight python >>> class ColspanColumn(column.NameColumn): ... ... weight = 999 ... ... def getColspan(self, item): ... # colspan condition ... if item.__name__ == 'first': ... return 2 ... else: ... return 0 ... ... def renderHeadCell(self): ... return u'Colspan' ... ... def renderCell(self, item): ... return u'colspan: %s' % item.title }}} Теперь зарегистрируем адаптер этого столбца как '''colspanColumn''': {{{#!highlight python >>> zope.component.provideAdapter(ColspanColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='colspanColumn') }}} Теперь вы видите, насколько объединение через '''!ColspanAdapter''' больше, чем столбцы таблицы. Такой код вызовет исключение '''!ValueError''': {{{#!highlight python >>> plainTable.update() ... ValueError: Colspan for column '' larger then table. }}} Но если мы установим столбец первой строчкой, таблица отрисуется корректно: {{{#!highlight python >>> class CorrectColspanColumn(ColspanColumn): ... """Colspan with correct weight.""" ... ... weight = 0 }}} Зарегистрируйте и отрисуйте таблицу еще раз: {{{#!highlight python >>> zope.component.provideAdapter(CorrectColspanColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='colspanColumn') >>> plainTable.update() >>> print plainTable.render() }}} {{{#!highlight html
Colspan Name Title
colspan: First Title: First
colspan: Second second Title: Second
colspan: Third third Title: Third
}}} == Установка колонок == Существующая реализация позволяет определять таблицу в классе без использования адаптеров. Сначала нужно определить столбец, который сможет отобразить значения наших элементов: {{{#!highlight python >>> class SimpleColumn(column.Column): ... ... weight = 0 ... ... def renderCell(self, item): ... return item.title }}} Давайте определим нашу таблицу, которая явно определяет столбцы. Вы также можете увидеть, что мы не возвращаем колонки в правильном порядке: {{{#!highlight python >>> class PrivateTable(table.Table): ... ... def setUpColumns(self): ... firstColumn = TitleColumn(self.context, self.request, self) ... firstColumn.__name__ = u'title' ... firstColumn.weight = 1 ... secondColumn = SimpleColumn(self.context, self.request, self) ... secondColumn.__name__ = u'simple' ... secondColumn.weight = 2 ... secondColumn.header = u'The second column' ... return [secondColumn, firstColumn] }}} Теперь мы можете создавать, обновлять и отображать таблицу: {{{#!highlight python >>> privateTable = PrivateTable(folder, request) >>> privateTable.update() >>> print privateTable.render() }}} {{{#!highlight html
Title The second column
Title: First First
Title: Second Second
Title: Third Third
}}} == Каскадные таблицы стилей == Наша реализация таблицы и столбца поддерживает установку классов css. Давайте определим таблицу и столбцы с некими значениями css: {{{#!highlight python >>> class CSSTable(table.Table): ... ... cssClasses = {'table': 'table', ... 'thead': 'thead', ... 'tbody': 'tbody', ... 'th': 'th', ... 'tr': 'tr', ... 'td': 'td'} ... ... def setUpColumns(self): ... firstColumn = TitleColumn(self.context, self.request, self) ... firstColumn.__name__ = u'title' ... firstColumn.__parent__ = self ... firstColumn.weight = 1 ... firstColumn.cssClasses = {'th':'thCol', 'td':'tdCol'} ... secondColumn = SimpleColumn(self.context, self.request, self) ... secondColumn.__name__ = u'simple' ... secondColumn.__parent__ = self ... secondColumn.weight = 2 ... secondColumn.header = u'The second column' ... return [secondColumn, firstColumn] }}} Теперь посмотрит, как отображается такая таблица с учетом присвоенных значений css классов. Заметьте, что '''th''' и '''td''' получили из таблицы и из колонки. {{{#!highlight python >>> cssTable = CSSTable(folder, request) >>> cssTable.update() >>> print cssTable.render() }}} {{{#!highlight html
Title The second column
Title: First First
Title: Second Second
Title: Third Third
}}} == Перемежающиеся таблицы == Мы поддерживаем встроенную поддержку перемежающихся строк таблицы, которые основаны на парных и непарных классах CSS. Давайте определим таблицу, включая другие CSS классы. Для поддержки парности/непарности нам следует определить классу '''cssClassEven''' и '''cssClassOdd''' CSS: {{{#!highlight python >>> class AlternatingTable(table.Table): ... ... cssClasses = {'table': 'table', ... 'thead': 'thead', ... 'tbody': 'tbody', ... 'th': 'th', ... 'tr': 'tr', ... 'td': 'td'} ... ... cssClassEven = u'even' ... cssClassOdd = u'odd' ... ... def setUpColumns(self): ... firstColumn = TitleColumn(self.context, self.request, self) ... firstColumn.__name__ = u'title' ... firstColumn.__parent__ = self ... firstColumn.weight = 1 ... firstColumn.cssClasses = {'th':'thCol', 'td':'tdCol'} ... secondColumn = SimpleColumn(self.context, self.request, self) ... secondColumn.__name__ = u'simple' ... secondColumn.__parent__ = self ... secondColumn.weight = 2 ... secondColumn.header = u'The second column' ... return [secondColumn, firstColumn] }}} Теперь обновите и отобразите новую таблицу. Как видите, к данному '''tr''' классу добавились дополнительные классы '''even''' и '''odd''': {{{#!highlight python >>> alternatingTable = AlternatingTable(folder, request) >>> alternatingTable.update() >>> print alternatingTable.render() }}} {{{#!highlight html
Title The second column
Title: First First
Title: Second Second
Title: Third Third
}}} == Сортировка таблицы == Еще одно свойство таблицы - поддержка сортировки данных по столбцам. Так как сортировка данных таблицы вещь очень важная, мы предлагаем ее по умолчанию. Но ее можно использовать только тогда, когда установлено значение '''sortOn'''. Вы можете установить это значение на уровне класса, добавляя значение '''defaultSortOn''' или устанавливая его как значение в запросе. Мы покажем вам, как это сделать позже. Нам также потребуются столбцы, которые помогут продемонстрировать хороший пример сортировки. Наш новый столбец, по которому будем сортировать, будет использовать атрибут '''number''' элементов содержимого в качестве критерия сортировки: {{{#!highlight python >>> 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 }}} Теперь давайте установим таблицу: {{{#!highlight python >>> class SortingTable(table.Table): ... ... def setUpColumns(self): ... firstColumn = TitleColumn(self.context, self.request, self) ... firstColumn.__name__ = u'title' ... firstColumn.__parent__ = self ... secondColumn = NumberColumn(self.context, self.request, self) ... secondColumn.__name__ = u'number' ... secondColumn.__parent__ = self ... return [firstColumn, secondColumn] }}} Нам также потребуется больше элементов в контейнере, которые мы будем сортировать: {{{#!highlight python >>> folder[u'fourth'] = File('Fourth', 4) >>> folder[u'zero'] = File('Zero', 0) }}} Давайте отобразим из без установленного значения '''sortOn''': {{{#!highlight python >>> sortingTable = SortingTable(folder, request) >>> sortingTable.update() >>> print sortingTable.render() }}} {{{#!highlight html
Title Number
Title: First number: 1
Title: Fourth number: 4
Title: Second number: 2
Title: Third number: 3
Title: Zero number: 0
}}} Как видите, эта таблица не предоставляет никакого явного порядка. Давайте определим индекс столбца, по которому будем сортировать данные: {{{#!highlight python >>> sortOnId = sortingTable.rows[0][1][1].id >>> sortOnId u'table-number-1' }}} Теперь давайте используем найденный индекс как значение '''sortOn''': {{{#!highlight python >>> sortingTable.sortOn = sortOnId }}} Важным моментом является обновление таблицы после установки сортировки по значению: {{{#!highlight python >>> sortingTable.update() >>> print sortingTable.render() }}} {{{#!highlight html
Title Number
Title: Zero number: 0
Title: First number: 1
Title: Second number: 2
Title: Third number: 3
Title: Fourth number: 4
}}} Мы также можем инвертировать порядок сортировки: {{{#!highlight python >>> sortingTable.sortOrder = 'reverse' >>> sortingTable.update() >>> print sortingTable.render() }}} {{{#!highlight html
Title Number
Title: Fourth number: 4
Title: Third number: 3
Title: Second number: 2
Title: First number: 1
Title: Zero number: 0
}}} Реализация таблицы также позволяет установить критерий сортировки, который передается через запрос. Давайте настроим такой запрос: {{{#!highlight python >>> sorterRequest = TestRequest(form={'table-sortOn': 'table-number-1', ... 'table-sortOrder':'descending'}) }}} и еще раз обновим и отобразим. Как видите, новая таблица отсортирована по втором столбце и упорядоченна в инверсном порядке: {{{#highlight python >>> requestSortedTable = SortingTable(folder, sorterRequest) >>> requestSortedTable.update() >>> print requestSortedTable.render() }}} {{{#!highlight html
Title Number
Title: Fourth number: 4
Title: Third number: 3
Title: Second number: 2
Title: First number: 1
Title: Zero number: 0
}}} == Настройка таблицы на базе класса == Есть еще один красивый способ определить строки таблицы на уровне класса. Мы предлагаем метод, который вы можете использовать, если нужно определить несколько колонок, - это метод '''addColumn'''. Перед тем, как определить таблицу, давайте определим метод отображения ячеек: {{{#!highlight python >>> def headCellRenderer(): ... return u'My items' >>> def cellRenderer(item): ... return u'%s item' % item.title }}} Теперь мы можем определить нашу таблицу и использовать свой метод рисования ячеек: {{{#!highlight python >>> class AddColumnTable(table.Table): ... ... cssClasses = {'table': 'table', ... 'thead': 'thead', ... 'tbody': 'tbody', ... 'th': 'th', ... 'tr': 'tr', ... 'td': 'td'} ... ... cssClassEven = u'even' ... cssClassOdd = u'odd' ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, TitleColumn, u'title', ... cellRenderer=cellRenderer, ... headCellRenderer=headCellRenderer, ... weight=1, colspan=0), ... column.addColumn(self, SimpleColumn, name=u'simple', ... weight=2, header=u'The second column', ... cssClasses = {'th':'thCol', 'td':'tdCol'}) ... ] >>> addColumnTable = AddColumnTable(folder, request) >>> addColumnTable.update() >>> print addColumnTable.render() }}} {{{#!highlight html
My items The second column
First item First
Fourth item Fourth
Second item Second
Third item Third
Zero item Zero
}}} Как видите, столбцы таблицы предоставляют все атрибуты, которые были установлены в методе '''addColumn''': {{{#!highlight python >>> titleColumn = addColumnTable.rows[0][0][1] >>> titleColumn >>> titleColumn.__name__ u'title' >>> titleColumn.__parent__ >>> titleColumn.colspan 0 >>> titleColumn.weight 1 >>> titleColumn.header u'Title' >>> titleColumn.cssClasses {} }}} и вторая колонка {{{#!highlight python >>> simpleColumn = addColumnTable.rows[0][1][1] >>> simpleColumn >>> simpleColumn.__name__ u'simple' >>> simpleColumn.__parent__ >>> simpleColumn.colspan 0 >>> simpleColumn.weight 2 >>> simpleColumn.header u'The second column' >>> simpleColumn.cssClasses {'td': 'tdCol', 'th': 'thCol'} }}} == Порционное отображение данных == Таблица реализует концепцию порционного отображения данных из коробки. Если количество строк меньше заданного размера '''startBatchingAt''', таблица начнет разбиваться на этом размере. Давайте определим новую таблицу: Следующим шагом идет настройка провайдера порций. Посмотрите раздел '''!BatchProvider''' (ниже) для получения более подробной информации о порционном отображении данных: {{{#!highlight python >>> from zope.configuration.xmlconfig import XMLConfig >>> import zope.app.component >>> import z3c.table >>> XMLConfig('meta.zcml', zope.component)() >>> XMLConfig('configure.zcml', z3c.table)() >>> class BatchingTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, TitleColumn, u'title', ... cellRenderer=cellRenderer, ... headCellRenderer=headCellRenderer, ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ] }}} Теперь мы можем создать таблицу: {{{#!highlight python >>> batchingTable = BatchingTable(folder, request) }}} Нам также следует дать таблице расположение и имя, как принято при траверсинге: {{{#!highlight python >>> batchingTable.__parent__ = folder >>> batchingTable.__name__ = u'batchingTable.html' }}} Теперь добавим еще несколько элементов в папку: {{{#!highlight python >>> folder[u'sixth'] = File('Sixth', 6) >>> folder[u'seventh'] = File('Seventh', 7) >>> folder[u'eighth'] = File('Eighth', 8) >>> folder[u'ninth'] = File('Ninth', 9) >>> folder[u'tenth'] = File('Tenth', 10) >>> folder[u'eleventh'] = File('Eleventh', 11) >>> folder[u'twelfth '] = File('Twelfth', 12) >>> folder[u'thirteenth'] = File('Thirteenth', 13) >>> folder[u'fourteenth'] = File('Fourteenth', 14) >>> folder[u'fifteenth '] = File('Fifteenth', 15) >>> folder[u'sixteenth'] = File('Sixteenth', 16) >>> folder[u'seventeenth'] = File('Seventeenth', 17) >>> folder[u'eighteenth'] = File('Eighteenth', 18) >>> folder[u'nineteenth'] = File('Nineteenth', 19) >>> folder[u'twentieth'] = File('Twentieth', 20) }}} Теперь покажем полную таблицу: {{{#!highlight html >>> batchingTable.update() >>> print batchingTable.render()
My items Number
Eighteenth item number: 18
Eighth item number: 8
Eleventh item number: 11
Fifteenth item number: 15
First item number: 1
Fourteenth item number: 14
Fourth item number: 4
Nineteenth item number: 19
Ninth item number: 9
Second item number: 2
Seventeenth item number: 17
Seventh item number: 7
Sixteenth item number: 16
Sixth item number: 6
Tenth item number: 10
Third item number: 3
Thirteenth item number: 13
Twelfth item number: 12
Twentieth item number: 20
Zero item number: 0
}}} Как видите, таблица неупорядоченная и отображает все элементы. Если мы хотим разбить ее на порции, нам следует установить размер '''startBatchingAt''' меньшим, нежели он установлен по умолчанию. Значение по умолчанию - 50: {{{#!highlight python >>> batchingTable.startBatchingAt 50 }}} Мы начнем порцию на 5 элементе. Это значит, что первые 5 элементов не будут использоваться: {{{#!highlight python >>> batchingTable.startBatchingAt = 5 >>> batchingTable.startBatchingAt 5 }}} Есть также значение '''batchSize''', которое следует установить в 5. По умолчанию, значение этого атрибута - 50: {{{#!highlight python >>> batchingTable.batchSize 50 >>> batchingTable.batchSize = 5 >>> batchingTable.batchSize 5 }}} Теперь мы можем обновить и отобразить таблицу снова. Вы увидите только 5 строк таблицы, и это правильно. Порядок не зависит от чисел, которы мы видим в ячейках: {{{#!highlight python >>> batchingTable.update() >>> print batchingTable.render() }}} {{{#!highlight html
My items Number
Eighteenth item number: 18
Eighth item number: 8
Eleventh item number: 11
Fifteenth item number: 15
First item number: 1
}}} Я думаю нам следует сортировать таблицу по второму столбцу перед тем, как показывать следующие порции данных. Делаем мы это просто установив '''defaultSortOn''': {{{#!highlight python >>> batchingTable.sortOn = u'table-number-1' }}} Теперь мы должны увидеть хорошо отсортированную таблицу: {{{#!highlight python >>> batchingTable.update() >>> print batchingTable.render() }}} {{{#!highlight html
My items Number
Zero item number: 0
First item number: 1
Second item number: 2
Third item number: 3
Fourth item number: 4
}}} Концепция порций позволяет нам выбрать со всех порций только тот набор данных, который нам нужен. Сделать это мы можем установив любую порцию как строки. Как видите, у нас есть 4 строковые порции данных: {{{#!highlight python >>> len(batchingTable.rows.batches) 4 }}} Мы можем устанавливать такие порции как строки, тогда данные этот порции будут использоваться при отображении. Но будьте внимательны, так как если мы обновим таблицу, наши строки перекроют и обнулят предыдущие значения. Это значит, что вы можете установить любую порцию как строковые данные и отобразить только их. Это доступно, так как метод '''update''' отсортировал все элементы и порцию, которая содержит данные готовые для использования. Эта концепция очень важна, если вам нужно кешировать порции данных. {{{#!highlight python >>> batchingTable.rows = batchingTable.rows.batches[1] >>> print batchingTable.render() }}} {{{#!highlight html
My items Number
Sixth item number: 6
Seventh item number: 7
Eighth item number: 8
Ninth item number: 9
Tenth item number: 10
}}} Как говорилось выше, если вы вызываете метод '''update''' наши настройки порций строк обнуляются: {{{#!highlight python >>> batchingTable.update() >>> print batchingTable.render() }}} {{{#!highlight html
My items Number
Zero item number: 0
First item number: 1
Second item number: 2
Third item number: 3
Fourth item number: 4
}}} Это значит, что, возможно, вы можете обновить все порции, закешировать их, и использовать позже, но, обычно, это не очень полезно, так как нужно проектировать страницу специально для таких нужд. Это также значит, что должен быть еще один путь установить индекс порций данных. И он существует. Есть два других способа, с помощью которых можно установить положение порции. Мы можем установить положение порции с помощью значения '''batchStart''' в таблице, или использовать переменную запроса. Давайте сначала посмотрим первый вариант: {{{#!highlight python >>> batchingTable.batchStart = 6 >>> batchingTable.update() >>> print batchingTable.render() }}} {{{#!highlight html
My items Number
Seventh item number: 7
Eighth item number: 8
Ninth item number: 9
Tenth item number: 10
Eleventh item number: 11
}}} Мы также можем установить положение порции используя значение '''batchStart''' из запроса. Заметьте, что нам нужен префикс таблицы и '''_ _name_ _''' колонки, как было в случае с сортировкой: {{{#!highlight python >>> batchingRequest = TestRequest(form={'table-batchStart': '11', ... 'table-batchSize': '5', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = BatchingTable(folder, batchingRequest) }}} Нам также потребуется предоставить таблице положение (location) и имя, как обычно делается с траверсингом: {{{#!highlight python >>> requestBatchingTable.__parent__ = folder >>> requestBatchingTable.__name__ = u'requestBatchingTable.html' }}} {{{#!wiki note Наша таблица должна начать делать порции по меньшему количеству элементов, нежели у нас есть по умолчанию, в противном случае мы не получим порции. }}} {{{#!highlight python >>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.update() >>> print requestBatchingTable.render() }}} {{{#!highlight html
My items Number
Twelfth item number: 12
Thirteenth item number: 13
Fourteenth item number: 14
Fifteenth item number: 15
Sixteenth item number: 16
}}} == BatchProvider == Провайдер порций позволяет отображать порцию HTML независимо от таблицы. Это значит, что по умолчанию порция не отображается методом '''render'''. Вы можете поменять такое поведение в своей реализации таблицы и возвращать порцию и таблицу в методе '''render'''. Как видите, строки нашей таблицы предоставляют '''IBatch''': {{{#!highlight python >>> from z3c.batching.interfaces import IBatch >>> IBatch.providedBy(requestBatchingTable.rows) True }}} Давайте проверим переменные порций перед тем, как отобразить наши тестовые данные. Это позволяет нам сравнивать результаты отображения. Для получения более подробной информации о порциях обратитесь к '''README.txt''' в ''z3c.batching'': {{{#!highlight python >>> requestBatchingTable.rows.start 11 >>> requestBatchingTable.rows.index 2 >>> requestBatchingTable.rows.batches >>> len(requestBatchingTable.rows.batches) 4 }}} Мы используем предыдущую таблицу и отображаем порцию с помощью встроенного метода '''renderBatch''': {{{#!highlight python >>> requestBatchingTable.update() >>> print requestBatchingTable.renderBatch() }}} {{{#!highlight html 1 2 3 4 }}} Давайте теперь добавим еще элементов, чтобы протестировать пропущенные ссылки в больших порциях: {{{#!highlight python >>> for i in range(1000): ... idx = i+20 ... folder[str(idx)] = File(str(idx), idx) }}} Теперь протестируем таблицу снова с новым набором элементов и значением '''startBatchingAt''' - 5, но начиная порцию с элемента с позицией 100 и отсортированной по втором столбце: {{{#!highlight python >>> batchingRequest = TestRequest(form={'table-batchStart': '100', ... 'table-batchSize': '5', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = BatchingTable(folder, batchingRequest) >>> requestBatchingTable.startBatchingAt = 5 }}} Нам также следует дать таблице положение (location) и имя, как обычно делается при траверсинге: {{{#!highlight python >>> requestBatchingTable.__parent__ = folder >>> requestBatchingTable.__name__ = u'requestBatchingTable.html' >>> requestBatchingTable.update() >>> print requestBatchingTable.render() }}} {{{#!highlight html
My items Number
100 item number: 100
101 item number: 101
102 item number: 102
103 item number: 103
104 item number: 104
}}} И тестируем порцию. Заметьте, что три точки между ссылками отображаются провайдером порций и не являются частью доктеста: {{{#highlight python >>> print requestBatchingTable.renderBatch() }}} {{{#!highlight html 1 ... 18 19 20 21 22 23 24 ... 204 }}} Вы можете изменить разделитель в провайдере порций, установив значение '''batchSpacer''': {{{#!highlight python >>> from z3c.table.batch import BatchProvider >>> class XBatchProvider(BatchProvider): ... """Just another batch provider.""" ... batchSpacer = u'xxx' }}} Теперь зарегистрируйте новый провайдер порций: {{{#!highlight python >>> import zope.publisher.interfaces.browser >>> zope.component.provideAdapter(XBatchProvider, ... (zope.interface.Interface, ... zope.publisher.interfaces.browser.IBrowserRequest, ... BatchingTable), name='batch') }}} Если мы обновим и отобразим таблицу, будет использован новый провайдер порций. Как видите, разделитель изменился: {{{#!highlight python >>> requestBatchingTable.update() >>> print requestBatchingTable.renderBatch() }}} {{{#!highlight html 1 xxx 18 19 20 21 22 23 24 xxx 204 }}} Возможно, это не имеет смысла, но давайте посмотрит, что происходит, если мы установим предыдущий и следующий размер порции в 0 (ноль): {{{#!highlight python >>> from z3c.table.batch import BatchProvider >>> class ZeroBatchProvider(BatchProvider): ... """Just another batch provider.""" ... batchSpacer = u'xxx' ... previousBatchSize = 0 ... nextBatchSize = 0 }}} Теперь зарегистрируем новый провайдер порций: {{{#!highlight python >>> import zope.publisher.interfaces.browser >>> zope.component.provideAdapter(ZeroBatchProvider, ... (zope.interface.Interface, ... zope.publisher.interfaces.browser.IBrowserRequest, ... BatchingTable), name='batch') }}} Обновите таблицу и отобразите порцию: {{{#!highlight python >>> requestBatchingTable.update() >>> print requestBatchingTable.renderBatch() }}} {{{#!highlight html 1 xxx 21 xxx 204 }}} == SequenceTable == Таблица последовательности может быть использована, если нужно предоставить таблицу для последовательности элементов, вместо отображения (mapping). Определите такую же последовательность элементов, как мы использовали до того, как добавили 1000 элементов: {{{#!highlight python >>> dataSequence = [] >>> dataSequence.append(File('Zero', 0)) >>> dataSequence.append(File('First', 1)) >>> dataSequence.append(File('Second', 2)) >>> dataSequence.append(File('Third', 3)) >>> dataSequence.append(File('Fourth', 4)) >>> dataSequence.append(File('Fifth', 5)) >>> dataSequence.append(File('Sixth', 6)) >>> dataSequence.append(File('Seventh', 7)) >>> dataSequence.append(File('Eighth', 8)) >>> dataSequence.append(File('Ninth', 9)) >>> dataSequence.append(File('Tenth', 10)) >>> dataSequence.append(File('Eleventh', 11)) >>> dataSequence.append(File('Twelfth', 12)) >>> dataSequence.append(File('Thirteenth', 13)) >>> dataSequence.append(File('Fourteenth', 14)) >>> dataSequence.append(File('Fifteenth', 15)) >>> dataSequence.append(File('Sixteenth', 16)) >>> dataSequence.append(File('Seventeenth', 17)) >>> dataSequence.append(File('Eighteenth', 18)) >>> dataSequence.append(File('Nineteenth', 19)) >>> dataSequence.append(File('Twentieth', 20)) }}} Теперь давайте определим новую '''!SequenceTable''': {{{#!highlight python >>> class SequenceTable(table.SequenceTable): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, TitleColumn, u'title', ... cellRenderer=cellRenderer, ... headCellRenderer=headCellRenderer, ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ] }}} Теперь мы можем создать таблицу, адаптируя последовательность: {{{#!highlight python >>> sequenceRequest = TestRequest(form={'table-batchStart': '0', ... 'table-sortOn': 'table-number-1'}) >>> sequenceTable = SequenceTable(dataSequence, sequenceRequest) }}} Нам также потребуется дать имя и положение таблице: {{{#!highlight python >>> sequenceTable.__parent__ = folder >>> sequenceTable.__name__ = u'sequenceTable.html' }}} Обновите и отобразите таблицу последовательности: {{{#!highlight python >>> sequenceTable.update() >>> print sequenceTable.render() }}} {{{#!highlight html
My items Number
Zero item number: 0
First item number: 1
Second item number: 2
Third item number: 3
Fourth item number: 4
Fifth item number: 5
Sixth item number: 6
Seventh item number: 7
Eighth item number: 8
Ninth item number: 9
Tenth item number: 10
Eleventh item number: 11
Twelfth item number: 12
Thirteenth item number: 13
Fourteenth item number: 14
Fifteenth item number: 15
Sixteenth item number: 16
Seventeenth item number: 17
Eighteenth item number: 18
Nineteenth item number: 19
Twentieth item number: 20
}}} Как видите, элементы отобразились на базе последовательности данных. Теперь установим начальный размер порций в 5: {{{#!highlight python >>> sequenceTable.startBatchingAt = 5 }}} И размер порций в 5. {{{#!highlight python >>> sequenceTable.batchSize = 5 }}} Теперь мы можем обновить и отобразить таблицу снова. Но вы увидите, что мы получили таблицу только из 5 строк: {{{#!highlight python >>> sequenceTable.update() >>> print sequenceTable.render() }}} {{{#!highlight html
My items Number
Zero item number: 0
First item number: 1
Second item number: 2
Third item number: 3
Fourth item number: 4
}}} И мы установили порядок сортировки в инверсный даже при использовании порций: {{{#!highlight python >>> sequenceTable.sortOrder = u'reverse' >>> sequenceTable.update() >>> print sequenceTable.render() }}} {{{#!hihglight html
My items Number
Twentieth item number: 20
Nineteenth item number: 19
Eighteenth item number: 18
Seventeenth item number: 17
Sixteenth item number: 16
}}} == Заголовки == Мы можем изменить отображение заголовка , например, столбца '''Title''', зарегистрировав адаптер к '''IHeaderColumn'''. Это может быть полезно для добавления ссылок на заголовки столбцов существующих реализаций таблицы. Давайте использовать свежую, почти пустую папку: {{{#!hihglight python >>> 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 }}} Теперь мы может зарегистрировать адаптер столбца прямо на класс таблицы. {{{#!highlight python >>> zope.component.provideAdapter(TitleColumn, ... (None, None, myTableClass), provides=interfaces.IColumn, ... name='titleColumn') }} И добавляем регистрацию заголовка столбца - будем использовать предоставленную базовую реализацию сортировки. {{{#!highlight python >>> from z3c.table.header import SortingColumnHeader >>> zope.component.provideAdapter(SortingColumnHeader, ... (None, None, interfaces.ITable, interfaces.IColumn), ... provides=interfaces.IColumnHeader) }}} Теперь мы можем отобразить таблицу и увидим ссылку в заголовке. Заметьте, что он предназначен для переключения направления сортировки, так как таблица изначально будет отображать первую колонку по возрастанию. {{{#!highlight python >>> myTable.update() >>> print myTable.render() }}} {{{#!highlight html ...
Title
}}} == Другое == Давайте сделаем обзор веселым и протестируем разные вещи. Протестируем, возвращает ли метод '''getWeight''' 0 (ноль) при '''!AttributeError''': {{{#!highlight python >>> from z3c.table.table import getWeight >>> getWeight(None) 0 }}} Попробуйте вызвать простую таблицу и '''renderBatch''', который должен возвратить пустую строку: {{{#!highlight python >>> simpleTable = table.Table(folder, request) >>> simpleTable.renderBatch() u'' }}} Попробуйте отобразить пустую таблицу, которая адаптируется к пустому отображению (mapping): {{{#!highlight python >>> simpleTable = table.Table({}, request) >>> simpleTable.render() u'' }}} Давайте посмотрит, вызывает ли '''addColumn''' '''!ValueError''', если нет класса '''Column''': {{{#!highlight python >>> column.addColumn(simpleTable, column.Column, u'dummy') >>> column.addColumn(simpleTable, None, u'dummy') ... ValueError: class_ None must implement IColumn. }}} Протестируем, можно ли передавать дополнительные аргументы в '''addColumn''': {{{#!highlight python >>> simpleColumn = column.addColumn(simpleTable, column.Column, u'dummy', ... foo='foo value', bar=u'something else', counter=99) >>> simpleColumn.foo 'foo value' >>> simpleColumn.bar u'something else' >>> simpleColumn.counter 99 }}} Класс '''NoneCell''' предоставляет методы, которые никогда не используются. Но эти методы должны быть так, так как они определены в интерфейсе. Давайте протестируем значения по умолчанию. Давайте сначала возьмем объект из папки: {{{#!highlight python >>> firstItem = folder[u'first'] >>> noneCellColumn = column.addColumn(simpleTable, column.NoneCell, u'none') >>> noneCellColumn.renderCell(firstItem) u'' >>> noneCellColumn.getColspan(firstItem) 0 >>> noneCellColumn.renderHeadCell() u'' >>> noneCellColumn.renderCell(firstItem) u'' }}} Реализация столбца ('''Column''') по умолчанию вызывает '''!NotImplementedError''', если мы не переопределили метод '''renderCell''': {{{#!highlight python >>> defaultColumn = column.addColumn(simpleTable, column.Column, u'default') >>> defaultColumn.renderCell(firstItem) ... NotImplementedError: Subclass must implement renderCell }}} = 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
Name Number
first number: 1
fourth number: 4
second number: 2
third number: 3
zero number: 0
}}} == 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
X Number
number: 1
number: 4
number: 2
number: 3
number: 0
}}} Как видите, мы можем насильно отобразить поле опционного ввода, как выбрано в значении, что передается через переменную запроса: {{{#!highlight python >>> radioRequest = TestRequest(form={'table-radioColumn-0-selectedItem': 'third'}) >>> radioTable = RadioTable(container, radioRequest) >>> radioTable.update() >>> print radioTable.render() }}} {{{#!highlight html
X Number
number: 1
number: 4
number: 2
number: 3
number: 0
}}} == 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
X Number
number: 1
number: 4
number: 2
number: 3
number: 0
}}} И снова вы можете насильно отобразить поле ввода с галочкой, как указано в переменной запроса: {{{#!highlight python >>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems': ... ['first', 'third']}) >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest) >>> checkBoxTable.update() >>> print checkBoxTable.render() }}} {{{#!highlight html
X Number
number: 1
number: 4
number: 2
number: 3
number: 0
}}} Если вы выбираете строку, вы также можете предоставить дополнительный 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
X Number
number: 1
number: 4
number: 2
number: 3
number: 0
}}} Давайте протестируем '''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
X Number
number: 1
number: 4
number: 2
number: 3
number: 0
}}} == 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
Created
01/01/01 01:01
01/01/01 01:01
01/01/01 01:01
01/01/01 01:01
01/01/01 01:01
}}} == 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
Modified
02/02/02 02:02
02/02/02 02:02
02/02/02 02:02
02/02/02 02:02
02/02/02 02:02
}}} == 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
First
Fourth
Second
Third
Zero
}}} Если мы используем не существующий атрибут, то не получаете '''!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
missing
missing
missing
missing
missing
}}} Несуществующий '''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' }}} == LinkColumn == Давайте определим таблицу с использованием '''!LinkColumn'''. Этот столбец позволяет создавать столбцы, которые указывают на страницы, для которых отображаемый в таблице объект является контекстом: {{{#!highlight python >>> class MyLinkColumns(column.LinkColumn): ... linkName = 'myLink.html' ... linkTarget = '_blank' ... linkCSS = 'myClass' >>> class MyLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, MyLinkColumns, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ] }}} Теперь создадим, обновим и отобразим таблицу: {{{#!highlight python >>> from zope.publisher.browser import TestRequest >>> request = TestRequest() >>> myLinkTable = MyLinkTable(container, request) >>> myLinkTable.__parent__ = container >>> myLinkTable.__name__ = u'myLinkTable.html' >>> myLinkTable.update() >>> print myLinkTable.render() }}} {{{#!highlight html
Name Number
first number: 1
fourth number: 4
second number: 2
third number: 3
zero number: 0
}}} == ContentsLinkColumn == Существует несколько предопределенных колонок со ссылками. Эта генерирует ссылку на '''contents.html''' для каждого элемента: {{{#!highlight python >>> class ContentsLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.ContentsLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ] >>> contentsLinkTable = ContentsLinkTable(container, request) >>> contentsLinkTable.__parent__ = container >>> contentsLinkTable.__name__ = u'contentsLinkTable.html' >>> contentsLinkTable.update() >>> print contentsLinkTable.render() }}} {{{#!highlight html
Name Number
first number: 1
fourth number: 4
second number: 2
third number: 3
zero number: 0
}}} == IndexLinkColumn == Этот столбец генерирует ссылку на '''index.html''': {{{#!highlight python >>> class IndexLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.IndexLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ] >>> indexLinkTable = IndexLinkTable(container, request) >>> indexLinkTable.__parent__ = container >>> indexLinkTable.__name__ = u'indexLinkTable.html' >>> indexLinkTable.update() >>> print indexLinkTable.render() }}} {{{#!highlight html
Name Number
first number: 1
fourth number: 4
second number: 2
third number: 3
zero number: 0
}}} == EditLinkColumn == А этот столбец генерирует ссылку на '''edit.html''' для каждого элемента: {{{#!highlight python >>> class EditLinkTable(table.Table): ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.EditLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ] >>> editLinkTable = EditLinkTable(container, request) >>> editLinkTable.__parent__ = container >>> editLinkTable.__name__ = u'editLinkTable.html' >>> editLinkTable.update() >>> print editLinkTable.render() }}} {{{#!highlight html
Name Number
first number: 1
fourth number: 4
second number: 2
third number: 3
zero number: 0
}}} '''Перевод: Ростислав Дзинько'''