Zope3的模块化表格渲染实现
项目描述
此包提供Zope3的模块化表格渲染实现。
z3c Table
此包的目标是提供一个模块化表格渲染库。我们使用内容提供者模式和列适配器,这将为我们提供一个强大的基础概念。
我们使用的一些重要概念
在更新渲染部分中实现单独的实现,这允许我们在渲染之前和之后操作数据。
如果需要,允许使用页面模板。默认情况下,所有操作都在Python中完成。
允许在现有的表格HTML部分外使用渲染的批处理。
没有皮肤
此包不提供任何类型的模板或皮肤支持。大多数情况下,如果您需要渲染表格,您将使用自己的皮肤概念。这意味着您可以在自己的模板内渲染表格或批处理。这将确保我们在这个包中尽可能少地依赖项,并且包可以与任何皮肤概念一起重用。
注意
正如您可能知道的那样,批处理只能在排序列之后进行。这在性能方面可能是一个噩梦。原因在于,所有数据都需要在批处理从给定位置开始之前进行排序。而且,排序通常只能通过接触每个对象来实现。这意味着如果您使用大量数据,即使使用批处理,您也必须小心。
示例数据设置
让我们创建一个示例容器,我们可以将其用作迭代上下文
>>> from zope.container import btree >>> class Container(btree.BTreeContainer): ... """Sample container.""" ... __name__ = u'container' >>> container = Container()
并为容器设置一个父容器
>>> root['container'] = container
并创建一个示例内容对象,我们将其用作容器项
>>> class Content(object): ... """Sample content.""" ... def __init__(self, title, number): ... self.title = title ... self.number = number
现在设置一些项
>>> container[u'first'] = Content('First', 1) >>> container[u'second'] = Content('Second', 2) >>> container[u'third'] = Content('Third', 3)
表格
创建一个测试请求并表示表格
>>> from zope.publisher.browser import TestRequest >>> from z3c.table import table >>> request = TestRequest() >>> plainTable = table.Table(container, request) >>> plainTable.cssClassSortedOn = None
现在我们可以更新并渲染表格。如您所见,使用空容器,我们将无法得到任何看起来像表格的东西。我们只得到一个空字符串
>>> plainTable.update() >>> plainTable.render() u''
列适配器
我们可以为表格创建一个列
>>> import zope.component >>> from z3c.table import interfaces >>> from z3c.table import column>>> class TitleColumn(column.Column): ... ... weight = 10 ... header = u'Title' ... ... def renderCell(self, item): ... return u'Title: %s' % item.title
现在我们可以注册该列
>>> zope.component.provideAdapter(TitleColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='firstColumn')
现在我们可以再次渲染表格
>>> plainTable.update() >>> print(plainTable.render()) <table> <thead> <tr> <th>Title</th> </tr> </thead> <tbody> <tr> <td>Title: First</td> </tr> <tr> <td>Title: Second</td> </tr> <tr> <td>Title: Third</td> </tr> </tbody> </table>
我们还可以使用预定义的名称列
>>> zope.component.provideAdapter(column.NameColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='secondColumn')
现在我们将得到一个额外的列
>>> plainTable.update() >>> print(plainTable.render()) <table> <thead> <tr> <th>Name</th> <th>Title</th> </tr> </thead> <tbody> <tr> <td>first</td> <td>Title: First</td> </tr> <tr> <td>second</td> <td>Title: Second</td> </tr> <tr> <td>third</td> <td>Title: Third</td> </tr> </tbody> </table>
跨列
现在让我们展示如何为列定义跨列条件为2
>>> 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
>>> zope.component.provideAdapter(ColspanColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='colspanColumn')
现在您可以看到,跨列适配器的跨列大于表格。这将引发 ValueError
>>> plainTable.update() Traceback (most recent call last): ... ValueError: Colspan for column '<ColspanColumn u'colspanColumn'>' is larger than the table.
但是,如果我们设置列为第一行,它将正确渲染跨列
>>> class CorrectColspanColumn(ColspanColumn): ... """Colspan with correct weight.""" ... ... weight = -1 # NameColumn is 0
再次注册并渲染表格
>>> zope.component.provideAdapter(CorrectColspanColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='colspanColumn')>>> plainTable.update() >>> print(plainTable.render()) <table> <thead> <tr> <th>Colspan</th> <th>Name</th> <th>Title</th> </tr> </thead> <tbody> <tr> <td colspan="2">colspan: First</td> <td>Title: First</td> </tr> <tr> <td>colspan: Second</td> <td>second</td> <td>Title: Second</td> </tr> <tr> <td>colspan: Third</td> <td>third</td> <td>Title: Third</td> </tr> </tbody> </table>
设置列
现有的实现允许我们在不使用列的模块化适配器模式的情况下在类中定义表格。
首先,我们需要定义一个列,它可以为我们的事项渲染一个值
>>> class SimpleColumn(column.Column): ... ... weight = 0 ... ... def renderCell(self, item): ... return item.title
让我们定义我们的表格,它明确地定义了列。您还可以看到,我们没有按正确顺序返回列
>>> class PrivateTable(table.Table): ... cssClassSortedOn = None ... ... 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]
现在我们可以创建、更新和渲染表格,并看到这也渲染了一个漂亮的表格
>>> privateTable = PrivateTable(container, request) >>> privateTable.update() >>> print(privateTable.render()) <table> <thead> <tr> <th>Title</th> <th>The second column</th> </tr> </thead> <tbody> <tr> <td>Title: First</td> <td>First</td> </tr> <tr> <td>Title: Second</td> <td>Second</td> </tr> <tr> <td>Title: Third</td> <td>Third</td> </tr> </tbody> </table>
层叠样式表
我们的表格和列实现支持CSS类分配。让我们定义一个具有一些CSS类值的表格和列
>>> class CSSTable(table.Table): ... ... cssClasses = {'table': 'table', ... 'thead': 'thead', ... 'tbody': 'tbody', ... 'th': 'th', ... 'tr': 'tr', ... 'td': 'td'} ... cssClassSortedOn = None ... ... 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从表格和列中获得了CSS声明
>>> cssTable = CSSTable(container, request) >>> cssTable.update() >>> print(cssTable.render()) <table class="table"> <thead class="thead"> <tr class="tr"> <th class="thCol th">Title</th> <th class="th">The second column</th> </tr> </thead> <tbody class="tbody"> <tr class="tr"> <td class="tdCol td">Title: First</td> <td class="td">First</td> </tr> <tr class="tr"> <td class="tdCol td">Title: Second</td> <td class="td">Second</td> </tr> <tr class="tr"> <td class="tdCol td">Title: Third</td> <td class="td">Third</td> </tr> </tbody> </table>
交替表格
我们提供了基于偶数和奇数CSS类的交替表格行的内置支持。让我们定义一个包括其他CSS类的表格。对于偶数/奇数支持,我们只需要定义cssClassEven和cssClassOdd CSS类
>>> class AlternatingTable(table.Table): ... ... cssClasses = {'table': 'table', ... 'thead': 'thead', ... 'tbody': 'tbody', ... 'th': 'th', ... 'tr': 'tr', ... 'td': 'td'} ... ... cssClassEven = u'even' ... cssClassOdd = u'odd' ... cssClassSortedOn = None ... ... 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类被添加到偶数和奇数类中
>>> alternatingTable = AlternatingTable(container, request) >>> alternatingTable.update() >>> print(alternatingTable.render()) <table class="table"> <thead class="thead"> <tr class="tr"> <th class="thCol th">Title</th> <th class="th">The second column</th> </tr> </thead> <tbody class="tbody"> <tr class="even tr"> <td class="tdCol td">Title: First</td> <td class="td">First</td> </tr> <tr class="odd tr"> <td class="tdCol td">Title: Second</td> <td class="td">Second</td> </tr> <tr class="even tr"> <td class="tdCol td">Title: Third</td> <td class="td">Third</td> </tr> </tbody> </table>
基于类的表格设置
在类级别定义表格行有一种更优雅的方法。我们提供了一个方法,您可以在需要定义一些列时使用它,称为addColumn。在我们定义表格之前,让我们定义一些单元格渲染器
>>> def headCellRenderer(): ... return u'My items'>>> def cellRenderer(item): ... return u'%s item' % item.title
现在我们可以定义我们的表格并使用自定义单元格渲染器
>>> class AddColumnTable(table.Table): ... ... cssClasses = {'table': 'table', ... 'thead': 'thead', ... 'tbody': 'tbody', ... 'th': 'th', ... 'tr': 'tr', ... 'td': 'td'} ... ... cssClassEven = u'even' ... cssClassOdd = u'odd' ... cssClassSortedOn = None ... ... 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'}) ... ]
添加一些更多内容
>>> container[u'fourth'] = Content('Fourth', 4) >>> container[u'zero'] = Content('Zero', 0) >>> addColumnTable = AddColumnTable(container, request) >>> addColumnTable.update() >>> print(addColumnTable.render()) <table class="table"> <thead class="thead"> <tr class="tr"> <th class="th">My items</th> <th class="thCol th">The second column</th> </tr> </thead> <tbody class="tbody"> <tr class="even tr"> <td class="td">First item</td> <td class="tdCol td">First</td> </tr> <tr class="odd tr"> <td class="td">Fourth item</td> <td class="tdCol td">Fourth</td> </tr> <tr class="even tr"> <td class="td">Second item</td> <td class="tdCol td">Second</td> </tr> <tr class="odd tr"> <td class="td">Third item</td> <td class="tdCol td">Third</td> </tr> <tr class="even tr"> <td class="td">Zero item</td> <td class="tdCol td">Zero</td> </tr> </tbody> </table>
如您所见,表格列提供了我们在addColumn方法中设置的的所有属性
>>> titleColumn = addColumnTable.rows[0][0][1] >>> titleColumn <TitleColumn u'title'>>>> titleColumn.__name__ u'title'>>> titleColumn.__parent__ <AddColumnTable None>>>> titleColumn.colspan 0>>> titleColumn.weight 1>>> titleColumn.header u'Title'>>> titleColumn.cssClasses {}
和第二个列
>>> simpleColumn = addColumnTable.rows[0][1][1] >>> simpleColumn <SimpleColumn u'simple'>>>> simpleColumn.__name__ u'simple'>>> simpleColumn.__parent__ <AddColumnTable None>>>> simpleColumn.colspan 0>>> simpleColumn.weight 2>>> simpleColumn.header u'The second column'>>> sorted(simpleColumn.cssClasses.items()) [('td', 'tdCol'), ('th', 'thCol')]
表头
我们可以通过注册IHeaderColumn适配器来更改标题列等的渲染方式。这可能对于为现有表格实现添加列头链接很有用。
我们将使用一个几乎空的容器。
>>> container = Container() >>> root['container-1'] = container >>> container[u'first'] = Content('First', 1) >>> container[u'second'] = Content('Second', 2) >>> container[u'third'] = Content('Third', 3)>>> class myTableClass(table.Table): ... cssClassSortedOn = None>>> myTable = myTableClass(container, request)>>> class TitleColumn(column.Column): ... ... header = u'Title' ... weight = -2 ... ... def renderCell(self, item): ... return item.title
现在我们可以直接将列适配器注册到我们的表格类中
>>> zope.component.provideAdapter(TitleColumn, ... (None, None, myTableClass), provides=interfaces.IColumn, ... name='titleColumn')
添加一个列标题的注册 - 我们在这里将使用提供的通用排序标题实现
>>> from z3c.table.header import SortingColumnHeader >>> zope.component.provideAdapter(SortingColumnHeader, ... (None, None, interfaces.ITable, interfaces.IColumn), ... provides=interfaces.IColumnHeader)
现在我们可以渲染表格,我们将在标题中看到一个链接。注意,它被设置为在表格最初显示第一列为升序时切换到降序
>>> myTable.update() >>> print(myTable.render()) <table> <thead> <tr> <th><a href="?table-sortOn=table-titleColumn-0&table-sortOrder=descending" title="Sort">Title</a></th> ... </table>
如果表格最初设置为降序,链接应允许再次切换到升序
>>> myTable.sortOrder = 'descending' >>> print(myTable.render()) <table> <thead> <tr> <th><a href="?table-sortOn=table-titleColumn-0&table-sortOrder=ascending" title="Sort">Title</a></th> ... </table>
如果表格是升序但请求是降序,链接应允许再次切换到升序
>>> descendingRequest = TestRequest(form={'table-sortOn': 'table-titleColumn-0', ... 'table-sortOrder':'descending'}) >>> myTable = myTableClass(container, descendingRequest) >>> myTable.sortOrder = 'ascending' >>> myTable.update() >>> print(myTable.render()) <table> <thead> <tr> <th><a href="?table-sortOn=table-titleColumn-0&table-sortOrder=ascending" title="Sort">Title</a></th> ... </table>
排序表格
另一个表格功能是支持按列排序数据。由于排序表格数据是一个重要功能,我们默认提供此功能。但只有当设置了 sortOn 值时才会使用它。您可以在类级别通过添加 defaultSortOn 值来设置此值,或者将其设置为请求值。我们稍后向您展示如何做到这一点。我们还需要一个列,以便我们能够进行更好的排序示例。我们新的排序列将使用内容项的数字值进行排序
>>> from z3c.table import column, table >>> 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
现在让我们设置一个表格
>>> from z3c.table.testing import TitleColumn >>> 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]
创建一个容器
>>> from z3c.table.testing import OrderedContainer >>> container = OrderedContainer()
我们还需要一些容器项,我们可以使用它们进行排序
>>> from z3c.table.testing import Content >>> container[u'first'] = Content('First', 1) >>> container[u'second'] = Content('Second', 2) >>> container[u'third'] = Content('Third', 3) >>> container[u'fourth'] = Content('Fourth', 4) >>> container[u'zero'] = Content('Zero', 0)
并且渲染它们而不设置 sortOn 值
>>> from zope.publisher.browser import TestRequest >>> request = TestRequest() >>> sortingTable = SortingTable(container, request) >>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th class="sorted-on ascending">Title</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td class="sorted-on ascending">Title: First</td> <td>number: 1</td> </tr> <tr> <td class="sorted-on ascending">Title: Fourth</td> <td>number: 4</td> </tr> <tr> <td class="sorted-on ascending">Title: Second</td> <td>number: 2</td> </tr> <tr> <td class="sorted-on ascending">Title: Third</td> <td>number: 3</td> </tr> <tr> <td class="sorted-on ascending">Title: Zero</td> <td>number: 0</td> </tr> </tbody> </table>
Oops,嗯,默认情况下,表格按第一列升序排序。
>>> sortingTable.sortOn 0
现在关闭排序,我们现在得到原始顺序
>>> sortingTable.sortOn = None >>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th>Title</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Title: First</td> <td>number: 1</td> </tr> <tr> <td>Title: Second</td> <td>number: 2</td> </tr> <tr> <td>Title: Third</td> <td>number: 3</td> </tr> <tr> <td>Title: Fourth</td> <td>number: 4</td> </tr> <tr> <td>Title: Zero</td> <td>number: 0</td> </tr> </tbody> </table>
如您所见,这个表格没有提供任何明确的顺序。让我们找到我们想要排序的列的索引
>>> sortOnId = sortingTable.rows[0][1][1].id >>> sortOnId u'table-number-1'
然后让我们将此ID用作 sortOn 值
>>> sortingTable.sortOn = sortOnId
一个重要的事情是在设置 sortOn 值后更新表格
>>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th>Title</th> <th class="sorted-on ascending">Number</th> </tr> </thead> <tbody> <tr> <td>Title: Zero</td> <td class="sorted-on ascending">number: 0</td> </tr> <tr> <td>Title: First</td> <td class="sorted-on ascending">number: 1</td> </tr> <tr> <td>Title: Second</td> <td class="sorted-on ascending">number: 2</td> </tr> <tr> <td>Title: Third</td> <td class="sorted-on ascending">number: 3</td> </tr> <tr> <td>Title: Fourth</td> <td class="sorted-on ascending">number: 4</td> </tr> </tbody> </table>
我们还可以反转排序顺序
>>> sortingTable.sortOrder = 'reverse' >>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th>Title</th> <th class="sorted-on reverse">Number</th> </tr> </thead> <tbody> <tr> <td>Title: Fourth</td> <td class="sorted-on reverse">number: 4</td> </tr> <tr> <td>Title: Third</td> <td class="sorted-on reverse">number: 3</td> </tr> <tr> <td>Title: Second</td> <td class="sorted-on reverse">number: 2</td> </tr> <tr> <td>Title: First</td> <td class="sorted-on reverse">number: 1</td> </tr> <tr> <td>Title: Zero</td> <td class="sorted-on reverse">number: 0</td> </tr> </tbody> </table>
表格实现还可以获取请求中提供的排序标准。让我们设置这样的请求
>>> sorterRequest = TestRequest(form={'table-sortOn': 'table-number-1', ... 'table-sortOrder':'descending'})
再次更新和渲染。如您所见,新表格按第二列排序,并且顺序相反
>>> requestSortedTable = SortingTable(container, sorterRequest) >>> requestSortedTable.update() >>> print(requestSortedTable.render()) <table> <thead> <tr> <th>Title</th> <th class="sorted-on descending">Number</th> </tr> </thead> <tbody> <tr> <td>Title: Fourth</td> <td class="sorted-on descending">number: 4</td> </tr> <tr> <td>Title: Third</td> <td class="sorted-on descending">number: 3</td> </tr> <tr> <td>Title: Second</td> <td class="sorted-on descending">number: 2</td> </tr> <tr> <td>Title: First</td> <td class="sorted-on descending">number: 1</td> </tr> <tr> <td>Title: Zero</td> <td class="sorted-on descending">number: 0</td> </tr> </tbody> </table>
存在一个标题渲染器,它提供了一个方便的链接渲染,用于排序
>>> import zope.component >>> from z3c.table import interfaces >>> from z3c.table.header import SortingColumnHeader >>> zope.component.provideAdapter(SortingColumnHeader, ... (None, None, interfaces.ITable, interfaces.IColumn), ... provides=interfaces.IColumnHeader)
现在让我们看看各种排序
>>> request = TestRequest() >>> sortingTable = SortingTable(container, request) >>> sortingTable.update() >>> sortingTable.sortOn 0 >>> sortingTable.sortOrder u'ascending' >>> print(sortingTable.render()) <table> <thead> <tr> <th class="sorted-on ascending"><a href="?table-sortOn=table-title-0&table-sortOrder=descending" title="Sort">Title</a></th> <th><a href="?table-sortOn=table-number-1&table-sortOrder=ascending" title="Sort">Number</a></th> </tr> </thead> <tbody> <tr> <td class="sorted-on ascending">Title: First</td> <td>number: 1</td> </tr> <tr> <td class="sorted-on ascending">Title: Fourth</td> <td>number: 4</td> </tr> <tr> <td class="sorted-on ascending">Title: Second</td> <td>number: 2</td> </tr> <tr> <td class="sorted-on ascending">Title: Third</td> <td>number: 3</td> </tr> <tr> <td class="sorted-on ascending">Title: Zero</td> <td>number: 0</td> </tr> </tbody> </table>
让我们看看 number 列
>>> sortingTable.sortOn = u'table-number-1'>>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th><a href="?table-sortOn=table-title-0&table-sortOrder=ascending" title="Sort">Title</a></th> <th class="sorted-on ascending"><a href="?table-sortOn=table-number-1&table-sortOrder=descending" title="Sort">Number</a></th> </tr> </thead> <tbody> <tr> <td>Title: Zero</td> <td class="sorted-on ascending">number: 0</td> </tr> <tr> <td>Title: First</td> <td class="sorted-on ascending">number: 1</td> </tr> <tr> <td>Title: Second</td> <td class="sorted-on ascending">number: 2</td> </tr> <tr> <td>Title: Third</td> <td class="sorted-on ascending">number: 3</td> </tr> <tr> <td>Title: Fourth</td> <td class="sorted-on ascending">number: 4</td> </tr> </tbody> </table>
让我们看看 title 列,但降序
>>> sortingTable.sortOn = u'table-title-0' >>> sortingTable.sortOrder = 'descending'>>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th class="sorted-on descending"><a href="?table-sortOn=table-title-0&table-sortOrder=ascending" title="Sort">Title</a></th> <th><a href="?table-sortOn=table-number-1&table-sortOrder=descending" title="Sort">Number</a></th> </tr> </thead> <tbody> <tr> <td class="sorted-on descending">Title: Zero</td> <td>number: 0</td> </tr> <tr> <td class="sorted-on descending">Title: Third</td> <td>number: 3</td> </tr> <tr> <td class="sorted-on descending">Title: Second</td> <td>number: 2</td> </tr> <tr> <td class="sorted-on descending">Title: Fourth</td> <td>number: 4</td> </tr> <tr> <td class="sorted-on descending">Title: First</td> <td>number: 1</td> </tr> </tbody> </table>
边缘情况,当某人尝试一些奇怪的 sortOn 值时,不要失败
>>> sortingTable.sortOn = u'table-title-foobar' >>> sortingTable.sortOrder = 'ascending'>>> sortingTable.update() >>> print(sortingTable.render()) <table> <thead> <tr> <th><a href="?table-sortOn=table-title-0&table-sortOrder=ascending" title="Sort">Title</a></th> <th><a href="?table-sortOn=table-number-1&table-sortOrder=ascending" title="Sort">Number</a></th> </tr> </thead> <tbody> <tr> <td>Title: First</td> <td>number: 1</td> </tr> <tr> <td>Title: Fourth</td> <td>number: 4</td> </tr> <tr> <td>Title: Second</td> <td>number: 2</td> </tr> <tr> <td>Title: Third</td> <td>number: 3</td> </tr> <tr> <td>Title: Zero</td> <td>number: 0</td> </tr> </tbody> </table>
批处理
我们的表格实现了开箱即用的分批处理。如果行项的数量小于给定的 startBatchingAt 大小,表格将从这个大小开始分批。让我们定义一个新的表格。
首先,我们需要为下一步配置我们的批处理提供程序。有关批处理渲染的更多信息,请参阅下面的 BatchProvider 部分
>>> from zope.configuration.xmlconfig import XMLConfig >>> import z3c.table >>> import zope.component >>> XMLConfig('meta.zcml', zope.component)() >>> XMLConfig('configure.zcml', z3c.table)()
现在我们可以创建我们的表格
>>> from zope.publisher.browser import TestRequest >>> from z3c.table.testing import Container, Content, SimpleTable >>> container = Container() >>> root['container-1'] = container >>> request = TestRequest() >>> batchingTable = SimpleTable(container, request) >>> batchingTable.cssClassSortedOn = None
我们还需要为表格提供一个位置和名称,就像我们在遍历时通常设置的那样
>>> batchingTable.__parent__ = container >>> batchingTable.__name__ = u'batchingTable.html'
现在设置一些项
>>> 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) >>> container[u'sixth'] = Content('Sixth', 6) >>> container[u'seventh'] = Content('Seventh', 7) >>> container[u'eighth'] = Content('Eighth', 8) >>> container[u'ninth'] = Content('Ninth', 9) >>> container[u'tenth'] = Content('Tenth', 10) >>> container[u'eleventh'] = Content('Eleventh', 11) >>> container[u'twelfth '] = Content('Twelfth', 12) >>> container[u'thirteenth'] = Content('Thirteenth', 13) >>> container[u'fourteenth'] = Content('Fourteenth', 14) >>> container[u'fifteenth '] = Content('Fifteenth', 15) >>> container[u'sixteenth'] = Content('Sixteenth', 16) >>> container[u'seventeenth'] = Content('Seventeenth', 17) >>> container[u'eighteenth'] = Content('Eighteenth', 18) >>> container[u'nineteenth'] = Content('Nineteenth', 19) >>> container[u'twentieth'] = Content('Twentieth', 20)
现在让我们显示没有分批的完整表格
>>> batchingTable.update() >>> print(batchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Eighteenth item</td> <td>number: 18</td> </tr> <tr> <td>Eighth item</td> <td>number: 8</td> </tr> <tr> <td>Eleventh item</td> <td>number: 11</td> </tr> <tr> <td>Fifteenth item</td> <td>number: 15</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Fourteenth item</td> <td>number: 14</td> </tr> <tr> <td>Fourth item</td> <td>number: 4</td> </tr> <tr> <td>Nineteenth item</td> <td>number: 19</td> </tr> <tr> <td>Ninth item</td> <td>number: 9</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> <tr> <td>Seventeenth item</td> <td>number: 17</td> </tr> <tr> <td>Seventh item</td> <td>number: 7</td> </tr> <tr> <td>Sixteenth item</td> <td>number: 16</td> </tr> <tr> <td>Sixth item</td> <td>number: 6</td> </tr> <tr> <td>Tenth item</td> <td>number: 10</td> </tr> <tr> <td>Third item</td> <td>number: 3</td> </tr> <tr> <td>Thirteenth item</td> <td>number: 13</td> </tr> <tr> <td>Twelfth item</td> <td>number: 12</td> </tr> <tr> <td>Twentieth item</td> <td>number: 20</td> </tr> <tr> <td>Zero item</td> <td>number: 0</td> </tr> </tbody> </table>
如您所见,表格没有排序,它使用了所有项。如果我们想使用批处理,我们需要将 startBatchingAt 大小设置得比默认值低。默认值设置为 50
>>> batchingTable.startBatchingAt 50
现在我们将批处理开始设置为 5。这意味着前5项不会使用
>>> batchingTable.startBatchingAt = 5 >>> batchingTable.startBatchingAt 5
还有一个 batchSize 值,我们需要将其设置为 5。默认情况下,该值通过 batchSize 值初始化
>>> batchingTable.batchSize 50>>> batchingTable.batchSize = 5 >>> batchingTable.batchSize 5
现在我们可以再次更新和渲染表格。但您会看到我们只有5行表格大小,这是正确的。但顺序不取决于我们看到的单元格中的数字
>>> batchingTable.update() >>> print(batchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Eighteenth item</td> <td>number: 18</td> </tr> <tr> <td>Eighth item</td> <td>number: 8</td> </tr> <tr> <td>Eleventh item</td> <td>number: 11</td> </tr> <tr> <td>Fifteenth item</td> <td>number: 15</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> </tbody> </table>
我认为在展示下一批数据之前,我们应该按第二列对表格进行排序。我们只需简单地设置defaultSortOn
>>> batchingTable.sortOn = u'table-number-1'
现在我们应该看到一个有序且分批的表格
>>> batchingTable.update() >>> print(batchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> <tr> <td>Third item</td> <td>number: 3</td> </tr> <tr> <td>Fourth item</td> <td>number: 4</td> </tr> </tbody> </table>
分批的概念允许我们从所有批次中选择并渲染此批次的行。我们可以通过将任何批次设置为行来实现。正如你所见,我们有4个分批的行数据可用
>>> len(batchingTable.rows.batches) 4
我们可以将这样一个批次设置为行值,然后使用这些批次数据来渲染。但请注意,如果我们更新表格,我们的行将被覆盖并重置为以前的值。这意味着你可以将任何批次设置为行数据,并且只渲染它们。这是因为更新方法对所有项目进行了排序,所有批次都包含可供使用的数据。如果需要缓存批次等,这个概念可能很重要。
>>> batchingTable.rows = batchingTable.rows.batches[1] >>> print(batchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Sixth item</td> <td>number: 6</td> </tr> <tr> <td>Seventh item</td> <td>number: 7</td> </tr> <tr> <td>Eighth item</td> <td>number: 8</td> </tr> <tr> <td>Ninth item</td> <td>number: 9</td> </tr> <tr> <td>Tenth item</td> <td>number: 10</td> </tr> </tbody> </table>
正如上述描述的那样,如果你调用update我们的批次到行设置将被重置
>>> batchingTable.update() >>> print(batchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> <tr> <td>Third item</td> <td>number: 3</td> </tr> <tr> <td>Fourth item</td> <td>number: 4</td> </tr> </tbody> </table>
这意味着你可能可以更新所有批次,缓存它们并在之后使用它们。但这对于页面中的常规使用(该页面不包含此实现的一部分的增强概念)来说并不实用。这也意味着,必须有一种方法来设置批次索引。是的,有其他两种方法可以设置批次的定位。我们可以通过在我们的表格中设置batchStart值来设置批次定位,或者我们可以使用请求变量。让我们先看看第一种方法
>>> batchingTable.batchStart = 6 >>> batchingTable.update() >>> print(batchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Seventh item</td> <td>number: 7</td> </tr> <tr> <td>Eighth item</td> <td>number: 8</td> </tr> <tr> <td>Ninth item</td> <td>number: 9</td> </tr> <tr> <td>Tenth item</td> <td>number: 10</td> </tr> <tr> <td>Eleventh item</td> <td>number: 11</td> </tr> </tbody> </table>
我们也可以通过在请求中使用batchStart值来设置批次定位。请注意,我们需要表格prefix和列__name__,就像我们在排序概念中使用的那样
>>> batchingRequest = TestRequest(form={'table-batchStart': '11', ... 'table-batchSize': '5', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.cssClassSortedOn = None
我们还需要像在遍历中通常设置的那样,为表格提供一个位置和名称
>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'
注意:我们的表格需要从比默认数量更少的项开始分批,否则我们不会得到一个批次
>>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Twelfth item</td> <td>number: 12</td> </tr> <tr> <td>Thirteenth item</td> <td>number: 13</td> </tr> <tr> <td>Fourteenth item</td> <td>number: 14</td> </tr> <tr> <td>Fifteenth item</td> <td>number: 15</td> </tr> <tr> <td>Sixteenth item</td> <td>number: 16</td> </tr> </tbody> </table>
BatchProvider
批次提供程序允许我们独立于表格渲染批次HTML。这意味着默认情况下,批次不在渲染方法中渲染。你可以在自定义表格实现中更改这一点,并在渲染方法中返回批次和表格。
正如我们所见,我们的表格行在分批方面提供了IBatch
>>> from z3c.batching.interfaces import IBatch >>> IBatch.providedBy(requestBatchingTable.rows) True
在我们渲染测试之前,让我们检查一些批次变量。这让我们可以比较渲染结果。有关分批的更多信息,请参阅z3c.batching中的README.txt
>>> requestBatchingTable.rows.start 11>>> requestBatchingTable.rows.index 2>>> requestBatchingTable.rows.batches <z3c.batching.batch.Batches object at ...>>>> len(requestBatchingTable.rows.batches) 4
我们使用之前的分批表格,并用内置的renderBatch方法渲染批次
>>> requestBatchingTable.update() >>> print(requestBatchingTable.renderBatch()) <a href="...html?table-batchSize=5&table-batchStart=0&..." class="first">1</a> <a href="...html?table-batchSize=5&table-batchStart=5&...">2</a> <a href="...html?table-batchSize=5&table-batchStart=11&..." class="current">3</a> <a href="...html?table-batchSize=5&table-batchStart=15&..." class="last">4</a>
现在让我们添加更多项目,以便我们可以测试大批量的跳过链接
>>> for i in range(1000): ... idx = i+20 ... container[str(idx)] = Content(str(idx), idx)
现在让我们用新的项目数量和相同的startBatchingAt(5),但开始批次在项目100,按第二个编号列排序再次测试分批表格
>>> batchingRequest = TestRequest(form={'table-batchStart': '100', ... 'table-batchSize': '5', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.cssClassSortedOn = None
我们还需要为表格提供一个位置和名称,就像我们在遍历时通常设置的那样
>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'>>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>100 item</td> <td>number: 100</td> </tr> <tr> <td>101 item</td> <td>number: 101</td> </tr> <tr> <td>102 item</td> <td>number: 102</td> </tr> <tr> <td>103 item</td> <td>number: 103</td> </tr> <tr> <td>104 item</td> <td>number: 104</td> </tr> </tbody> </table>
并且测试批次。请注意,链接之间的三个点是由批次提供程序渲染的,并且不是doctest的一部分
>>> print(requestBatchingTable.renderBatch()) <a href="...html?table-batchSize=5&table-batchStart=0&table-sortOn=table-number-1" class="first">1</a> ... <a href="...html?table-batchSize=5&table-batchStart=85&table-sortOn=table-number-1">18</a> <a href="...html?table-batchSize=5&table-batchStart=90&table-sortOn=table-number-1">19</a> <a href="...html?table-batchSize=5&table-batchStart=95&table-sortOn=table-number-1">20</a> <a href="...html?table-batchSize=5&table-batchStart=100&table-sortOn=table-number-1" class="current">21</a> <a href="...html?table-batchSize=5&table-batchStart=105&table-sortOn=table-number-1">22</a> <a href="...html?table-batchSize=5&table-batchStart=110&table-sortOn=table-number-1">23</a> <a href="...html?table-batchSize=5&table-batchStart=115&table-sortOn=table-number-1">24</a> ... <a href="...html?table-batchSize=5&table-batchStart=1015&table-sortOn=table-number-1" class="last">204</a>
如果你设置了batchSpacer值,你可以更改批次提供程序中的空格
>>> from z3c.table.batch import BatchProvider >>> from z3c.table.interfaces import IBatchProvider >>> from zope.interface import implementer >>> @implementer(IBatchProvider) ... class XBatchProvider(BatchProvider): ... """Just another batch provider.""" ... batchSpacer = u'xxx'
现在为新批次的表格注册新的批次提供程序
>>> import zope.publisher.interfaces.browser >>> from zope.component import getSiteManager >>> sm = getSiteManager(container) >>> sm.registerAdapter(XBatchProvider, ... (zope.interface.Interface, ... zope.publisher.interfaces.browser.IBrowserRequest, ... SimpleTable), name='batch')
如果我们更新并渲染我们的表格,新批次提供程序应该被使用。正如你所见,空格现在已更改
>>> requestBatchingTable.update() >>> requestBatchingTable.batchProvider <...XBatchProvider object at ...> >>> print(requestBatchingTable.renderBatch()) <a href="...html?table-batchSize=5&table-batchStart=0&table-sortOn=table-number-1" class="first">1</a> xxx <a href="...html?table-batchSize=5&table-batchStart=85&table-sortOn=table-number-1">18</a> <a href="...html?table-batchSize=5&table-batchStart=90&table-sortOn=table-number-1">19</a> <a href="...html?table-batchSize=5&table-batchStart=95&table-sortOn=table-number-1">20</a> <a href="...html?table-batchSize=5&table-batchStart=100&table-sortOn=table-number-1" class="current">21</a> <a href="...html?table-batchSize=5&table-batchStart=105&table-sortOn=table-number-1">22</a> <a href="...html?table-batchSize=5&table-batchStart=110&table-sortOn=table-number-1">23</a> <a href="...html?table-batchSize=5&table-batchStart=115&table-sortOn=table-number-1">24</a> xxx <a href="...html?table-batchSize=5&table-batchStart=1015&table-sortOn=table-number-1" class="last">204</a>
现在测试极端情况,需要定义一个新的分批请求:从左端点开始
>>> leftBatchingRequest = TestRequest(form={'table-batchStart': '10', ... 'table-batchSize': '5', ... 'table-sortOn': 'table-number-1'}) >>> leftRequestBatchingTable = SimpleTable(container, leftBatchingRequest) >>> leftRequestBatchingTable.__parent__ = container >>> leftRequestBatchingTable.__name__ = u'leftRequestBatchingTable.html' >>> leftRequestBatchingTable.update() >>> print(leftRequestBatchingTable.renderBatch()) <a href="http://...html?table-batchSize=5&table-batchStart=0&table-sortOn=table-number-1" class="first">1</a> <a href="http://...html?table-batchSize=5&table-batchStart=5&table-sortOn=table-number-1">2</a> <a href="http://...html?table-batchSize=5&table-batchStart=10&table-sortOn=table-number-1" class="current">3</a> <a href="http://...html?table-batchSize=5&table-batchStart=15&table-sortOn=table-number-1">4</a> <a href="http://...html?table-batchSize=5&table-batchStart=20&table-sortOn=table-number-1">5</a> <a href="http://...html?table-batchSize=5&table-batchStart=25&table-sortOn=table-number-1">6</a> xxx <a href="http://...html?table-batchSize=5&table-batchStart=1015&table-sortOn=table-number-1" class="last">204</a>
继续使用右端点
>>> rightBatchingRequest = TestRequest(form={'table-batchStart': '1005', ... 'table-batchSize': '5', ... 'table-sortOn': 'table-number-1'}) >>> rightRequestBatchingTable = SimpleTable(container, rightBatchingRequest) >>> rightRequestBatchingTable.__parent__ = container >>> rightRequestBatchingTable.__name__ = u'rightRequestBatchingTable.html' >>> rightRequestBatchingTable.update() >>> print(rightRequestBatchingTable.renderBatch()) <a href="http://...html?table-batchSize=5&table-batchStart=0&table-sortOn=table-number-1" class="first">1</a> xxx <a href="http://...html?table-batchSize=5&table-batchStart=990&table-sortOn=table-number-1">199</a> <a href="http://...html?table-batchSize=5&table-batchStart=995&table-sortOn=table-number-1">200</a> <a href="http://...html?table-batchSize=5&table-batchStart=1000&table-sortOn=table-number-1">201</a> <a href="http://...html?table-batchSize=5&table-batchStart=1005&table-sortOn=table-number-1" class="current">202</a> <a href="http://...html?table-batchSize=5&table-batchStart=1010&table-sortOn=table-number-1">203</a> <a href="http://...html?table-batchSize=5&table-batchStart=1015&table-sortOn=table-number-1" class="last">204</a>
没有上一个和下一个批次的大小。这可能没有意义,但让我们看看如果我们将上一个和下一个批次大小设置为0(零)会发生什么
>>> from z3c.table.batch import BatchProvider >>> class ZeroBatchProvider(BatchProvider): ... """Just another batch provider.""" ... batchSpacer = u'xxx' ... previousBatchSize = 0 ... nextBatchSize = 0
现在为新批次的表格注册新的批次提供程序
>>> import zope.publisher.interfaces.browser >>> sm.registerAdapter(ZeroBatchProvider, ... (zope.interface.Interface, ... zope.publisher.interfaces.browser.IBrowserRequest, ... SimpleTable), name='batch')
更新表格并渲染批次
>>> requestBatchingTable.update() >>> print(requestBatchingTable.renderBatch()) <a href="...html?table-batchSize=5&table-batchStart=0&table-sortOn=table-number-1" class="first">1</a> xxx <a href="...html?table-batchSize=5&table-batchStart=100&table-sortOn=table-number-1" class="current">21</a> xxx <a href="...html?table-batchSize=5&table-batchStart=1015&table-sortOn=table-number-1" class="last">204</a>
边缘情况,当有人尝试一些奇怪的批次值时,不要硬失败
>>> batchingRequest = TestRequest(form={'table-batchStart': '11', ... 'table-batchSize': 'foobar', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.cssClassSortedOn = None>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html' >>> requestBatchingTable.batchSize = 3>>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Twelfth item</td> <td>number: 12</td> </tr> <tr> <td>Thirteenth item</td> <td>number: 13</td> </tr> <tr> <td>Fourteenth item</td> <td>number: 14</td> </tr> </tbody> </table>>>> batchingRequest = TestRequest(form={'table-batchStart': '0', ... 'table-batchSize': '-10', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.cssClassSortedOn = None>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'>>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.batchSize = 3 >>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> </tbody> </table>>>> batchingRequest = TestRequest(form={'table-batchStart': 'foobar', ... 'table-batchSize': '3', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.cssClassSortedOn = None>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'>>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> </tbody> </table>>>> batchingRequest = TestRequest(form={'table-batchStart': '99999', ... 'table-batchSize': '3', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.cssClassSortedOn = None>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html'>>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>1017 item</td> <td>number: 1017</td> </tr> <tr> <td>1018 item</td> <td>number: 1018</td> </tr> <tr> <td>1019 item</td> <td>number: 1019</td> </tr> </tbody> </table>>>> batchingRequest = TestRequest(form={'table-batchStart': '-10', ... 'table-batchSize': 'foobar', ... 'table-sortOn': 'table-number-1'}) >>> requestBatchingTable = SimpleTable(container, batchingRequest) >>> requestBatchingTable.cssClassSortedOn = None>>> requestBatchingTable.__parent__ = container >>> requestBatchingTable.__name__ = u'requestBatchingTable.html' >>> requestBatchingTable.batchSize = 3>>> requestBatchingTable.startBatchingAt = 5 >>> requestBatchingTable.update() >>> print(requestBatchingTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> </tbody> </table>
SequenceTable
如果需要提供一个物品序列的表格而不是映射,可以使用序列表。定义我们在添加其他1000个物品之前使用的相同序列的物品
>>> from z3c.table.testing import Content >>> dataSequence = [] >>> dataSequence.append(Content('Zero', 0)) >>> dataSequence.append(Content('First', 1)) >>> dataSequence.append(Content('Second', 2)) >>> dataSequence.append(Content('Third', 3)) >>> dataSequence.append(Content('Fourth', 4)) >>> dataSequence.append(Content('Fifth', 5)) >>> dataSequence.append(Content('Sixth', 6)) >>> dataSequence.append(Content('Seventh', 7)) >>> dataSequence.append(Content('Eighth', 8)) >>> dataSequence.append(Content('Ninth', 9)) >>> dataSequence.append(Content('Tenth', 10)) >>> dataSequence.append(Content('Eleventh', 11)) >>> dataSequence.append(Content('Twelfth', 12)) >>> dataSequence.append(Content('Thirteenth', 13)) >>> dataSequence.append(Content('Fourteenth', 14)) >>> dataSequence.append(Content('Fifteenth', 15)) >>> dataSequence.append(Content('Sixteenth', 16)) >>> dataSequence.append(Content('Seventeenth', 17)) >>> dataSequence.append(Content('Eighteenth', 18)) >>> dataSequence.append(Content('Nineteenth', 19)) >>> dataSequence.append(Content('Twentieth', 20))
现在让我们定义一个新的SequenceTable
>>> from z3c.table import table, column >>> from z3c.table.testing import (TitleColumn, NumberColumn, cellRenderer, ... headCellRenderer) >>> 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') ... ]
现在我们可以创建我们的表格,并适应我们的序列
>>> from zope.publisher.browser import TestRequest >>> sequenceRequest = TestRequest(form={'table-batchStart': '0', ... 'table-sortOn': 'table-number-1'}) >>> sequenceTable = SequenceTable(dataSequence, sequenceRequest) >>> sequenceTable.cssClassSortedOn = None
我们还需要为表格提供一个位置和名称,就像我们在遍历时通常设置的那样
>>> from z3c.table.testing import Container >>> container = Container() >>> root['container-1'] = container >>> sequenceTable.__parent__ = container >>> sequenceTable.__name__ = u'sequenceTable.html'
首先,我们需要为下一步配置我们的批处理提供程序。有关批处理渲染的更多信息,请参阅下面的 BatchProvider 部分
>>> from zope.configuration.xmlconfig import XMLConfig >>> import z3c.table >>> import zope.component >>> XMLConfig('meta.zcml', zope.component)() >>> XMLConfig('configure.zcml', z3c.table)()
然后更新和渲染序列表
>>> sequenceTable.update() >>> print(sequenceTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> <tr> <td>Third item</td> <td>number: 3</td> </tr> <tr> <td>Fourth item</td> <td>number: 4</td> </tr> <tr> <td>Fifth item</td> <td>number: 5</td> </tr> <tr> <td>Sixth item</td> <td>number: 6</td> </tr> <tr> <td>Seventh item</td> <td>number: 7</td> </tr> <tr> <td>Eighth item</td> <td>number: 8</td> </tr> <tr> <td>Ninth item</td> <td>number: 9</td> </tr> <tr> <td>Tenth item</td> <td>number: 10</td> </tr> <tr> <td>Eleventh item</td> <td>number: 11</td> </tr> <tr> <td>Twelfth item</td> <td>number: 12</td> </tr> <tr> <td>Thirteenth item</td> <td>number: 13</td> </tr> <tr> <td>Fourteenth item</td> <td>number: 14</td> </tr> <tr> <td>Fifteenth item</td> <td>number: 15</td> </tr> <tr> <td>Sixteenth item</td> <td>number: 16</td> </tr> <tr> <td>Seventeenth item</td> <td>number: 17</td> </tr> <tr> <td>Eighteenth item</td> <td>number: 18</td> </tr> <tr> <td>Nineteenth item</td> <td>number: 19</td> </tr> <tr> <td>Twentieth item</td> <td>number: 20</td> </tr> </tbody> </table>
如您所见,项目基于数据序列进行渲染。现在我们将start batch at的大小设置为5
>>> sequenceTable.startBatchingAt = 5
并将batchSize设置为5
>>> sequenceTable.batchSize = 5
现在我们可以再次更新和渲染表格。但您会看到我们只得到5行大小的表格
>>> sequenceTable.update() >>> print(sequenceTable.render()) <table> <thead> <tr> <th>My items</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>Zero item</td> <td>number: 0</td> </tr> <tr> <td>First item</td> <td>number: 1</td> </tr> <tr> <td>Second item</td> <td>number: 2</td> </tr> <tr> <td>Third item</td> <td>number: 3</td> </tr> <tr> <td>Fourth item</td> <td>number: 4</td> </tr> </tbody> </table>
即使我们使用分批处理,我们也将排序顺序设置为reverse
>>> sequenceTable.sortOrder = u'reverse' >>> sequenceTable.update() >>> 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>
表格列
让我们展示我们默认提供的不同列。但在那之前,请先看一下README.txt,它解释了表格和列的概念。
示例数据设置
让我们创建一个示例容器,我们可以将其用作可迭代的上下文
>>> from zope.container import btree >>> class Container(btree.BTreeContainer): ... """Sample container.""" >>> container = Container() >>> root['container'] = container
并创建一个示例内容对象,我们将其用作容器项
>>> class Content(object): ... """Sample content.""" ... def __init__(self, title, number, email): ... self.title = title ... self.number = number ... self.email = email
现在设置一些项
>>> container[u'zero'] = Content('Zero', 0, 'zero@example.com') >>> container[u'first'] = Content('First', 1, 'first@example.com') >>> container[u'second'] = Content('Second', 2, 'second@example.com') >>> container[u'third'] = Content('Third', 3, 'third@example.com') >>> container[u'fourth'] = Content('Fourth', 4, None)
让我们也创建一个简单的可排序数字列
>>> 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定义一个表格
>>> from z3c.table import table >>> class NameTable(table.Table): ... cssClassSortedOn = None ... ... 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()概念渲染项目的名称
>>> from zope.publisher.browser import TestRequest >>> request = TestRequest() >>> nameTable = NameTable(container, request) >>> nameTable.update() >>> print(nameTable.render()) <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td>first</td> <td>number: 1</td> </tr> <tr> <td>fourth</td> <td>number: 4</td> </tr> <tr> <td>second</td> <td>number: 2</td> </tr> <tr> <td>third</td> <td>number: 3</td> </tr> <tr> <td>zero</td> <td>number: 0</td> </tr> </tbody> </table>
RadioColumn
让我们使用RadioColumn定义一个表格
>>> class RadioTable(table.Table): ... cssClassSortedOn = None ... ... 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') ... ]
现在创建、更新和渲染我们的表格
>>> request = TestRequest() >>> radioTable = RadioTable(container, request) >>> radioTable.update() >>> print(radioTable.render()) <table> <thead> <tr> <th>X</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="first" /></td> <td>number: 1</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="fourth" /></td> <td>number: 4</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="second" /></td> <td>number: 2</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="third" /></td> <td>number: 3</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="zero" /></td> <td>number: 0</td> </tr> </tbody> </table>
如您所见,我们可以通过给定的请求值强制将单选按钮渲染为选中状态
>>> radioRequest = TestRequest(form={'table-radioColumn-0-selectedItem': 'third'}) >>> radioTable = RadioTable(container, radioRequest) >>> radioTable.update() >>> print(radioTable.render()) <table> <thead> <tr> <th>X</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="first" /></td> <td>number: 1</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="fourth" /></td> <td>number: 4</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="second" /></td> <td>number: 2</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="third" checked="checked" /></td> <td>number: 3</td> </tr> <tr> <td><input type="radio" class="radio-widget" name="table-radioColumn-0-selectedItem" value="zero" /></td> <td>number: 0</td> </tr> </tbody> </table>
CheckBoxColumn
让我们使用RadioColumn定义一个表格
>>> class CheckBoxTable(table.Table): ... cssClassSortedOn = None ... ... 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') ... ]
现在创建、更新和渲染我们的表格
>>> request = TestRequest() >>> checkBoxTable = CheckBoxTable(container, request) >>> checkBoxTable.update() >>> print(checkBoxTable.render()) <table> <thead> <tr> <th>X</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" /></td> <td>number: 1</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td> <td>number: 4</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td> <td>number: 2</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" /></td> <td>number: 3</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td> <td>number: 0</td> </tr> </tbody> </table>
同样,您还可以将复选框输入字段强制渲染为选中状态,使用给定的请求值
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems': ... ['first', 'third']}) >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest) >>> checkBoxTable.update() >>> print(checkBoxTable.render()) <table> <thead> <tr> <th>X</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td> <td>number: 1</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td> <td>number: 4</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td> <td>number: 2</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td> <td>number: 3</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td> <td>number: 0</td> </tr> </tbody> </table>
如果您选择一行,您还可以为它们提供额外的CSS样式。这可以与交替的even和odd样式结合使用
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems': ... ['first', 'third']}) >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest) >>> checkBoxTable.cssClasses = {'tr': 'tr'} >>> checkBoxTable.cssClassSelected = u'selected' >>> checkBoxTable.cssClassEven = u'even' >>> checkBoxTable.cssClassOdd = u'odd' >>> checkBoxTable.update() >>> print(checkBoxTable.render()) <table> <thead> <tr class="tr"> <th>X</th> <th>Number</th> </tr> </thead> <tbody> <tr class="selected even tr"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td> <td>number: 1</td> </tr> <tr class="odd tr"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td> <td>number: 4</td> </tr> <tr class="even tr"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td> <td>number: 2</td> </tr> <tr class="selected odd tr"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td> <td>number: 3</td> </tr> <tr class="even tr"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td> <td>number: 0</td> </tr> </tbody> </table>
让我们测试没有其他CSS类的cssClassSelected
>>> checkBoxRequest = TestRequest(form={'table-checkBoxColumn-0-selectedItems': ... ['first', 'third']}) >>> checkBoxTable = CheckBoxTable(container, checkBoxRequest) >>> checkBoxTable.cssClassSelected = u'selected' >>> checkBoxTable.update() >>> print(checkBoxTable.render()) <table> <thead> <tr> <th>X</th> <th>Number</th> </tr> </thead> <tbody> <tr class="selected"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="first" checked="checked" /></td> <td>number: 1</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="fourth" /></td> <td>number: 4</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="second" /></td> <td>number: 2</td> </tr> <tr class="selected"> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="third" checked="checked" /></td> <td>number: 3</td> </tr> <tr> <td><input type="checkbox" class="checkbox-widget" name="table-checkBoxColumn-0-selectedItems" value="zero" /></td> <td>number: 0</td> </tr> </tbody> </table>
CreatedColumn
让我们使用CreatedColumn定义一个表格
>>> class CreatedColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.CreatedColumn, u'createdColumn', ... weight=1), ... ]
现在创建、更新和渲染我们的表格。注意,我们使用Dublin Core stub适配器,它只返回01/01/01 01:01作为创建日期
>>> request = TestRequest() >>> createdColumnTable = CreatedColumnTable(container, request) >>> createdColumnTable.update() >>> print(createdColumnTable.render()) <table> <thead> <tr> <th>Created</th> </tr> </thead> <tbody> <tr> <td>01/01/01 01:01</td> </tr> <tr> <td>01/01/01 01:01</td> </tr> <tr> <td>01/01/01 01:01</td> </tr> <tr> <td>01/01/01 01:01</td> </tr> <tr> <td>01/01/01 01:01</td> </tr> </tbody> </table>
ModifiedColumn
让我们使用CreatedColumn定义一个表格
>>> class ModifiedColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.ModifiedColumn, ... u'modifiedColumn', weight=1), ... ]
现在创建、更新和渲染我们的表格。注意,我们使用Dublin Core stub适配器,它只返回02/02/02 02:02作为修改日期
>>> request = TestRequest() >>> modifiedColumnTable = ModifiedColumnTable(container, request) >>> modifiedColumnTable.update() >>> print(modifiedColumnTable.render()) <table> <thead> <tr> <th>Modified</th> </tr> </thead> <tbody> <tr> <td>02/02/02 02:02</td> </tr> <tr> <td>02/02/02 02:02</td> </tr> <tr> <td>02/02/02 02:02</td> </tr> <tr> <td>02/02/02 02:02</td> </tr> <tr> <td>02/02/02 02:02</td> </tr> </tbody> </table>
GetAttrColumn
GetAttrColumn列是一个方便的列,它通过属性访问从项目检索值。它还在发生异常的情况下提供defaultValue。
>>> class GetTitleColumn(column.GetAttrColumn): ... ... attrName = 'title' ... defaultValue = u'missing'>>> class GetAttrColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, GetTitleColumn, u'title'), ... ]
渲染并更新表格
>>> request = TestRequest() >>> getAttrColumnTable = GetAttrColumnTable(container, request) >>> getAttrColumnTable.update() >>> print(getAttrColumnTable.render()) <table> <thead> <tr> <th></th> </tr> </thead> <tbody> <tr> <td>First</td> </tr> <tr> <td>Fourth</td> </tr> <tr> <td>Second</td> </tr> <tr> <td>Third</td> </tr> <tr> <td>Zero</td> </tr> </tbody> </table>
如果我们使用一个不存在的属性,我们不会引发AttributeError,我们将得到默认值
>>> class UndefinedAttributeColumn(column.GetAttrColumn): ... ... attrName = 'undefined' ... defaultValue = u'missing'>>> class GetAttrColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, UndefinedAttributeColumn, u'missing'), ... ]
渲染并更新表格
>>> request = TestRequest() >>> getAttrColumnTable = GetAttrColumnTable(container, request) >>> getAttrColumnTable.update() >>> print(getAttrColumnTable.render()) <table> <thead> <tr> <th></th> </tr> </thead> <tbody> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> </tbody> </table>
GetAttrColumn中的attrName缺失也会导致返回defaultValue
>>> class BadAttributeColumn(column.GetAttrColumn): ... ... defaultValue = u'missing'>>> firstItem = container[u'first'] >>> simpleTable = table.Table(container, request) >>> badColumn = column.addColumn(simpleTable, BadAttributeColumn, u'bad') >>> badColumn.renderCell(firstItem) u'missing'
如果我们尝试访问受保护的属性,对象会引发Unauthorized。在这种情况下,我们也会返回defaultValue。让我们设置一个对象,如果我们访问标题,它会引发这样的错误
>>> from zope.security.interfaces import Unauthorized >>> class ProtectedItem(object): ... ... @property ... def forbidden(self): ... raise Unauthorized('forbidden')
设置并测试项目
>>> protectedItem = ProtectedItem() >>> protectedItem.forbidden Traceback (most recent call last): ... Unauthorized: forbidden
现在定义一个列
>>> class ForbiddenAttributeColumn(column.GetAttrColumn): ... ... attrName = 'forbidden' ... defaultValue = u'missing'
并测试属性访问
>>> simpleTable = table.Table(container, request) >>> badColumn = column.addColumn(simpleTable, ForbiddenAttributeColumn, u'x') >>> badColumn.renderCell(protectedItem) u'missing'
GetItemColumn
GetItemColumn列是一个方便的列,它通过索引或键访问从项目检索值。这意味着项目可以是元组、列表、字典或任何实现该功能的东西。它还在发生异常的情况下提供defaultValue。
Dict-ish
>>> sampleDictData = [ ... dict(name='foo', value=1), ... dict(name='bar', value=7), ... dict(name='moo', value=42),]>>> class GetDictColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.GetItemColumn, u'name', ... header=u'Name', ... idx='name', defaultValue='missing'), ... column.addColumn(self, column.GetItemColumn, u'value', ... header=u'Value', ... idx='value', defaultValue='missing'), ... ] ... @property ... def values(self): ... return sampleDictData
渲染并更新表格
>>> request = TestRequest() >>> getDictColumnTable = GetDictColumnTable(sampleDictData, request) >>> getDictColumnTable.update() >>> print(getDictColumnTable.render()) <table> <thead> <tr> <th>Name</th> <th>Value</th> </tr> </thead> <tbody> <tr> <td>bar</td> <td>7</td> </tr> <tr> <td>foo</td> <td>1</td> </tr> <tr> <td>moo</td> <td>42</td> </tr> </tbody> </table>
如果我们使用一个不存在的索引/键,我们不会引发异常,我们将得到默认值
>>> class GetDictColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.GetItemColumn, u'name', ... idx='not-existing', defaultValue='missing'), ... ] ... @property ... def values(self): ... return sampleDictData
渲染并更新表格
>>> request = TestRequest() >>> getDictColumnTable = GetDictColumnTable(container, request) >>> getDictColumnTable.update() >>> print(getDictColumnTable.render()) <table> <thead> <tr> <th></th> </tr> </thead> <tbody> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> </tbody> </table>
GetItemColumn中的idx缺失也会导致返回defaultValue
>>> class BadIdxColumn(column.GetItemColumn): ... ... defaultValue = u'missing'>>> firstItem = sampleDictData[0] >>> simpleTable = table.Table(sampleDictData, request) >>> badColumn = column.addColumn(simpleTable, BadIdxColumn, u'bad') >>> badColumn.renderCell(firstItem) u'missing'
Tuple/List-ish
>>> sampleTupleData = [ ... (50, 'bar'), ... (42, 'cent'), ... (7, 'bild'),]>>> class GetTupleColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.GetItemColumn, u'name', ... header=u'Name', ... idx=1, defaultValue='missing'), ... column.addColumn(self, column.GetItemColumn, u'value', ... header=u'Value', ... idx=0, defaultValue='missing'), ... ] ... @property ... def values(self): ... return sampleTupleData
渲染并更新表格
>>> request = TestRequest() >>> getTupleColumnTable = GetTupleColumnTable(sampleTupleData, request) >>> getTupleColumnTable.update() >>> print(getTupleColumnTable.render()) <table> <thead> <tr> <th>Name</th> <th>Value</th> </tr> </thead> <tbody> <tr> <td>bar</td> <td>50</td> </tr> <tr> <td>bild</td> <td>7</td> </tr> <tr> <td>cent</td> <td>42</td> </tr> </tbody> </table>
如果我们使用一个不存在的索引/键,我们不会引发异常,我们将得到默认值
>>> class GetTupleColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.GetItemColumn, u'name', ... idx=42, defaultValue='missing'), ... ] ... @property ... def values(self): ... return sampleTupleData
渲染并更新表格
>>> request = TestRequest() >>> getTupleColumnTable = GetTupleColumnTable(container, request) >>> getTupleColumnTable.update() >>> print(getTupleColumnTable.render()) <table> <thead> <tr> <th></th> </tr> </thead> <tbody> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> <tr> <td>missing</td> </tr> </tbody> </table>
GetItemColumn中的idx缺失也会导致返回defaultValue
>>> class BadIdxColumn(column.GetItemColumn): ... ... defaultValue = u'missing'>>> firstItem = sampleTupleData[0] >>> simpleTable = table.Table(sampleTupleData, request) >>> badColumn = column.addColumn(simpleTable, BadIdxColumn, u'bad') >>> badColumn.renderCell(firstItem) u'missing'
GetAttrFormatterColumn
GetAttrFormatterColumn 列是一个获取属性列,可以格式化值。让我们使用 Dublin Core 适配器作为示例。
>>> from zope.dublincore.interfaces import IZopeDublinCore >>> class GetCreatedColumn(column.GetAttrFormatterColumn): ... ... def getValue(self, item): ... dc = IZopeDublinCore(item, None) ... return dc.created>>> class GetAttrFormatterColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, GetCreatedColumn, u'created'), ... ]
渲染并更新表格
>>> request = TestRequest() >>> getAttrFormatterColumnTable = GetAttrFormatterColumnTable(container, ... request) >>> getAttrFormatterColumnTable.update() >>> print(getAttrFormatterColumnTable.render()) <table> <thead> <tr> <th></th> </tr> </thead> <tbody> <tr> <td>2001 1 1 01:01:01 </td> </tr> <tr> <td>2001 1 1 01:01:01 </td> </tr> <tr> <td>2001 1 1 01:01:01 </td> </tr> <tr> <td>2001 1 1 01:01:01 </td> </tr> <tr> <td>2001 1 1 01:01:01 </td> </tr> </tbody> </table>
我们还可以更改该列的格式化设置。
>>> class LongCreatedColumn(column.GetAttrFormatterColumn): ... ... formatterCategory = u'dateTime' ... formatterLength = u'long' ... formatterCalendar = u'gregorian' ... ... def getValue(self, item): ... dc = IZopeDublinCore(item, None) ... return dc.created>>> class LongFormatterColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, LongCreatedColumn, u'created'), ... ]
渲染并更新表格
>>> request = TestRequest() >>> longFormatterColumnTable = LongFormatterColumnTable(container, ... request) >>> longFormatterColumnTable.update() >>> print(longFormatterColumnTable.render()) <table> <thead> <tr> <th></th> </tr> </thead> <tbody> <tr> <td>2001 1 1 01:01:01 +000</td> </tr> <tr> <td>2001 1 1 01:01:01 +000</td> </tr> <tr> <td>2001 1 1 01:01:01 +000</td> </tr> <tr> <td>2001 1 1 01:01:01 +000</td> </tr> <tr> <td>2001 1 1 01:01:01 +000</td> </tr> </tbody> </table>
EMailColumn
EMailColumn 列是 GetAttrColumn,用于显示一个 mailto 链接。默认情况下,链接内容中也会显示电子邮件地址。
>>> class EMailColumn(column.EMailColumn): ... ... attrName = 'email' ... defaultValue = u'missing'>>> class EMailColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, EMailColumn, u'email'), ... ]
当单元格不包含电子邮件地址时,将渲染 defaultValue。
>>> request = TestRequest() >>> eMailColumnTable = EMailColumnTable(container, request) >>> eMailColumnTable.update() >>> print(eMailColumnTable.render()) <table> <thead> <tr> <th>E-Mail</th> </tr> </thead> <tbody> <tr> <td><a href="mailto:first@example.com">first@example.com</a></td> </tr> <tr> <td><a href="mailto:second@example.com">second@example.com</a></td> </tr> <tr> <td><a href="mailto:third@example.com">third@example.com</a></td> </tr> <tr> <td><a href="mailto:zero@example.com">zero@example.com</a></td> </tr> <tr> <td>missing</td> </tr> </tbody> </table>
可以通过设置 linkContent 属性来覆盖链接内容。
>>> class StaticEMailColumn(column.EMailColumn): ... ... attrName = 'email' ... defaultValue = u'missing' ... linkContent = 'Mail me'>>> class StaticEMailColumnTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, StaticEMailColumn, u'mail'), ... ]
渲染并更新表格
>>> request = TestRequest() >>> staticEMailColumnTable = StaticEMailColumnTable(container, request) >>> staticEMailColumnTable.update() >>> print(staticEMailColumnTable.render()) <table> <thead> <tr> <th>E-Mail</th> </tr> </thead> <tbody> <tr> <td><a href="mailto:first@example.com">Mail me</a></td> </tr> <tr> <td><a href="mailto:second@example.com">Mail me</a></td> </tr> <tr> <td><a href="mailto:third@example.com">Mail me</a></td> </tr> <tr> <td><a href="mailto:zero@example.com">Mail me</a></td> </tr> <tr> <td>missing</td> </tr> </tbody> </table>
LinkColumn
让我们使用 LinkColumn 定义一个表格。此列允许我们编写指向具有项目上下文的页面的列。
>>> class MyLinkColumns(column.LinkColumn): ... linkName = 'myLink.html' ... linkTarget = '_blank' ... linkCSS = 'myClass' ... linkTitle = 'Click >'>>> class MyLinkTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, MyLinkColumns, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]
现在创建、更新和渲染我们的表格
>>> from zope.publisher.browser import TestRequest >>> request = TestRequest() >>> myLinkTable = MyLinkTable(container, request) >>> myLinkTable.__parent__ = container >>> myLinkTable.__name__ = u'myLinkTable.html' >>> myLinkTable.update() >>> print(myLinkTable.render()) <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/myLink.html" target="_blank" class="myClass" title="Click >">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/myLink.html" target="_blank" class="myClass" title="Click >">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/myLink.html" target="_blank" class="myClass" title="Click >">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/myLink.html" target="_blank" class="myClass" title="Click >">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/myLink.html" target="_blank" class="myClass" title="Click >">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>
ContentsLinkColumn
有一些预定义的链接列可供使用。此列将为每个项目生成 contents.html 链接。
>>> class ContentsLinkTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.ContentsLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]>>> contentsLinkTable = ContentsLinkTable(container, request) >>> contentsLinkTable.__parent__ = container >>> contentsLinkTable.__name__ = u'contentsLinkTable.html' >>> contentsLinkTable.update() >>> print(contentsLinkTable.render()) <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/contents.html">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/contents.html">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/contents.html">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/contents.html">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/contents.html">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>
IndexLinkColumn
此列将为每个项目生成 index.html 链接。
>>> class IndexLinkTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.IndexLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]>>> indexLinkTable = IndexLinkTable(container, request) >>> indexLinkTable.__parent__ = container >>> indexLinkTable.__name__ = u'indexLinkTable.html' >>> indexLinkTable.update() >>> print(indexLinkTable.render()) <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/index.html">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/index.html">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/index.html">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/index.html">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/index.html">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>
EditLinkColumn
此列将为每个项目生成 edit.html 链接。
>>> class EditLinkTable(table.Table): ... cssClassSortedOn = None ... ... def setUpColumns(self): ... return [ ... column.addColumn(self, column.EditLinkColumn, u'link', ... weight=1), ... column.addColumn(self, NumberColumn, name=u'number', ... weight=2, header=u'Number') ... ]>>> editLinkTable = EditLinkTable(container, request) >>> editLinkTable.__parent__ = container >>> editLinkTable.__name__ = u'editLinkTable.html' >>> editLinkTable.update() >>> print(editLinkTable.render()) <table> <thead> <tr> <th>Name</th> <th>Number</th> </tr> </thead> <tbody> <tr> <td><a href="http://127.0.0.1/container/first/edit.html">first</a></td> <td>number: 1</td> </tr> <tr> <td><a href="http://127.0.0.1/container/fourth/edit.html">fourth</a></td> <td>number: 4</td> </tr> <tr> <td><a href="http://127.0.0.1/container/second/edit.html">second</a></td> <td>number: 2</td> </tr> <tr> <td><a href="http://127.0.0.1/container/third/edit.html">third</a></td> <td>number: 3</td> </tr> <tr> <td><a href="http://127.0.0.1/container/zero/edit.html">zero</a></td> <td>number: 0</td> </tr> </tbody> </table>
杂项
让覆盖率报告满意,并测试不同的事情。
测试 getWeight 方法在 AttributeError 上是否返回 0(零)。
>>> from z3c.table.table import getWeight >>> getWeight(None) 0
创建一个容器
>>> from z3c.table.testing import Container >>> container = Container()
尝试调用一个简单的表格并调用 renderBatch,它应该返回一个空字符串。
>>> from z3c.table import table >>> from zope.publisher.browser import TestRequest >>> request = TestRequest() >>> simpleTable = table.Table(container, request) >>> simpleTable.renderBatch() u''
尝试使用空映射适配一个空表格进行渲染。
>>> simpleTable = table.Table({}, request) >>> simpleTable.cssClassSortedOn = None >>> simpleTable.render() u''
因为我们注册了一个 IColumn 适配器在 None(即空映射)上。
>>> from zope.component import provideAdapter >>> from z3c.table import column >>> from z3c.table import interfaces >>> provideAdapter(column.NameColumn, ... (None, None, interfaces.ITable), provides=interfaces.IColumn, ... name='secondColumn')
为空表格初始化行定义初始化了列属性列表。
>>> simpleTable.columns>>> simpleTable.initColumns() >>> simpleTable.columns [<NameColumn u'secondColumn'>]
现在渲染空表格返回一个字符串。
>>> print(simpleTable.render()) <table> <thead> <tr> <th>Name</th> </tr> </thead> <tbody> </tbody> </table>
让我们看看如果没有 Column 类,addColumn 是否会引发 ValueError。
>>> column.addColumn(simpleTable, column.Column, u'dummy') <Column u'dummy'>>>> column.addColumn(simpleTable, None, u'dummy') Traceback (most recent call last): ... ValueError: class_ None must implement IColumn.
测试是否可以在 addColumn 中设置额外的 kws。
>>> 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 类提供了一些从未被调用的方法。但这些方法在接口中已定义。让我们测试默认值并让覆盖率报告满意。
首先获取一个容器项。
>>> from z3c.table.testing import Content >>> firstItem = Content('First', 1) >>> noneCellColumn = column.addColumn(simpleTable, column.NoneCell, u'none') >>> noneCellColumn.renderCell(firstItem) u''>>> noneCellColumn.getColspan(firstItem) 0>>> noneCellColumn.renderHeadCell() u''>>> noneCellColumn.renderCell(firstItem) u''
默认的 Column 实现如果未重写 renderCell 方法将引发 NotImplementedError。
>>> defaultColumn = column.addColumn(simpleTable, column.Column, u'default') >>> defaultColumn.renderCell(firstItem) Traceback (most recent call last): ... NotImplementedError: Subclass must implement renderCell
CHANGES
3.0 (2023-03-31)
添加对 Python 3.11 的支持。
停止支持 Python 2.7、3.5 和 3.6。
在请求参数中防止不良输入 - 不要硬失败,使用默认值。
2.2 (2022-02-11)
添加对 Python 3.8、3.9 和 3.10 的支持。
2.1.1 (2019-03-26)
修复:在 Column.renderHeadCell、NameColumn.getName、CheckBoxColumn 名称和值、RadioColumn 名称和值、LinkColumn href 和链接内容中转义特殊 HTML 字符。
2.1 (2019-01-27)
添加了对 Python 3.7 和 PyPy3 的支持。
停止使用 python setup.py test 运行测试。
使用 black 和 flake8 重新格式化代码。
2.0.1 (2017-04-19)
需要 future>=0.14.0,以便在 Python 2.7 中使用 html 包。
2.0.0 (2017-04-17)
更新以仅支持 Python 2.7、3.5 和 3.6。
在可排序表格的列标题中添加了链接标题的翻译。
2.0.0a1 (2013-02-26)
添加了对 Python 3.3 的支持,停止支持 Python 2.5 及以下版本。
消除了对 z3.testing 和 zope.app.testing 的测试依赖。
1.0.0 (2012-08-09)
添加了排序(cssClassSortedOn 和 getCSSSortClass)CSS 选项。
添加了单元格高亮(getCSSHighlightClass)CSS 选项。
添加了 GetItemColumn,通过索引/键访问获取值。
0.9.1 (2011-08-03)
修复了 SelectedItemColumn.update,当只选择一个项目时。
0.9.0 (2010-08-09)
添加了 EMailColumn,可用于显示 mailto 链接。
修复了默认的 BatchProvider,防止在生成的链接中丢失表格排序查询参数;现在分批和排序可以很好地协同工作。
将单个 doctest 文件(README.txt)拆分为不同的文件。
0.8.1 (2010-07-31)
在可排序表格的列标题中添加了链接标题的翻译。
0.8.0 (2009-12-29)
添加了对 LinkColumn.linkContent 的翻译。
添加了 I18nGetAttrColumn,它翻译其内容。
0.7.0 (2009-12-29)
允许在不更新整个表格的情况下初始化列定义。
修复了测试,因此它们不再使用 zope.app.container(它甚至没有被声明为测试依赖项)。
现在已将表头单元格内容翻译。
0.6.1 (2009-02-22)
如果我们根据列中的 __name__ 值查找,则不要智能地使用 IPhysicallyLocatable 对象。
0.6.0 (2008-11-12)
修复:允许在标题链接上切换排序顺序。这阻止了第一次点击后变为降序。
修复:CheckBoxColumn,确保我们始终使用列表来比较所选项目。如果只选择了一个项目,则可能比较一个字符串。如果这个字符串是另一个现有项目的子串,则另一个项目也会被选中。
将高级分批实现移动到 z3c.batching。
实现了 GetAttrFormatterColumn。此列可用于简单值格式化列。
在 columns.py 中有错误拼写:将 getLinkConent 重命名为 getLinkContent。
错误:更改了 getLinkCSS 中的返回字符串。它使用 css="” 而不是 class="” 用于 CSS 类。感谢 Dan 报告此错误。
实现了 SelectedItemColumn。
修复 CheckBoxColumn,始终使用正确的 selectedItems。始终使用来自表格的实际 selectedItems。
修复 RadioColumn,始终使用 selectedItems 列表中的正确 selectedItem。始终使用来自表格的 selectedItems 列表中的第一个 selectedItems。
0.5.0 (2008-04-13)
初始发布。
项目详情
下载文件
为您的平台下载文件。如果您不确定要选择哪个,请了解更多关于 安装软件包 的信息。
源分发
构建分发
z3c.table-3.0.tar.gz 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | c9b4b3b5d9b0595a84a4f3be07c19f6a175e439cc93f7c12eb01e26e400f0540 |
|
MD5 | f47830762a7ebc1d699adf48665ac2c1 |
|
BLAKE2b-256 | 9278083b2c78de8d3ccc0fc9532a49c2652942ab73edb955c0a55dd2f49f59d4 |
z3c.table-3.0-py3-none-any.whl 的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 1415cfdee487fcd88bda140d9106655f260d241e8d0b075cd9379465097cc794 |
|
MD5 | 00ea20495135f75e5e156fc4f00e05da |
|
BLAKE2b-256 | a099fcaee322a70f0545a6591404dca9999d3cfc06a310b2af90e1160c57cb0c |