extend/adapt result set to use client instance objects for rows, using the applyTemplate() method for retrieving and formatting cell data
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2743 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
74b9efffc3
commit
66faec4a38
7 changed files with 82 additions and 35 deletions
|
@ -33,7 +33,7 @@ from zope.i18n.locales import locales
|
||||||
|
|
||||||
from cybertools.composer.base import Component
|
from cybertools.composer.base import Component
|
||||||
from cybertools.composer.schema.interfaces import IField, IFieldInstance
|
from cybertools.composer.schema.interfaces import IField, IFieldInstance
|
||||||
from cybertools.composer.schema.interfaces import fieldTypes
|
from cybertools.composer.schema.interfaces import fieldTypes, undefined
|
||||||
from cybertools.composer.schema.schema import formErrors
|
from cybertools.composer.schema.schema import formErrors
|
||||||
from cybertools.util.format import toStr, toUnicode
|
from cybertools.util.format import toStr, toUnicode
|
||||||
|
|
||||||
|
@ -61,7 +61,8 @@ class Field(Component):
|
||||||
def __init__(self, name, title=None, fieldType='textline', **kw):
|
def __init__(self, name, title=None, fieldType='textline', **kw):
|
||||||
assert name
|
assert name
|
||||||
self.__name__ = name
|
self.__name__ = name
|
||||||
title = title or u''
|
#title = title or u''
|
||||||
|
title = title or name
|
||||||
self.fieldType = fieldType
|
self.fieldType = fieldType
|
||||||
super(Field, self).__init__(title, __name__=name, **kw)
|
super(Field, self).__init__(title, __name__=name, **kw)
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -127,6 +128,7 @@ class FieldInstance(object):
|
||||||
adapts(IField)
|
adapts(IField)
|
||||||
|
|
||||||
clientInstance = None
|
clientInstance = None
|
||||||
|
value = undefined
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
|
@ -39,6 +39,7 @@ class Instance(BaseInstance):
|
||||||
|
|
||||||
aspect = 'schema.editor.default'
|
aspect = 'schema.editor.default'
|
||||||
template = None
|
template = None
|
||||||
|
view = None
|
||||||
|
|
||||||
def applyTemplate(self, *args, **kw):
|
def applyTemplate(self, *args, **kw):
|
||||||
#result = dict(__name__=self.context.__name__)
|
#result = dict(__name__=self.context.__name__)
|
||||||
|
|
|
@ -192,6 +192,8 @@ class IField(IComponent):
|
||||||
'field instance.')
|
'field instance.')
|
||||||
|
|
||||||
|
|
||||||
|
undefined = object() # A marker for a field instance value not set.
|
||||||
|
|
||||||
class IFieldInstance(Interface):
|
class IFieldInstance(Interface):
|
||||||
""" An adapter for checking and converting data values coming
|
""" An adapter for checking and converting data values coming
|
||||||
from or being displayed on an external system (like a browser form).
|
from or being displayed on an external system (like a browser form).
|
||||||
|
@ -201,18 +203,28 @@ class IFieldInstance(Interface):
|
||||||
name = Attribute('Field name.')
|
name = Attribute('Field name.')
|
||||||
change = Attribute('A tuple ``(oldValue, newValue)`` or None.')
|
change = Attribute('A tuple ``(oldValue, newValue)`` or None.')
|
||||||
errors = Attribute('A sequence of error infos.')
|
errors = Attribute('A sequence of error infos.')
|
||||||
severity = Attribute("An integer giving a state or error "
|
severity = Attribute('An integer giving a state or error '
|
||||||
"code, 0 meaning 'OK'.")
|
'code, 0 meaning OK.')
|
||||||
|
clientInstance = Attribute('An optional adapter to a client object that '
|
||||||
|
'provides or receives data processed by this field instance.')
|
||||||
|
value = Attribute ('May contain the current value of the field '
|
||||||
|
'for later reuse. Default is ``undefined``. '
|
||||||
|
'If the ``change`` attribute is set ``value`` should '
|
||||||
|
'be equal to ``change[1]``.')
|
||||||
|
|
||||||
def getRawValue(data, key, default=None):
|
def getRawValue(data, key, default=None):
|
||||||
""" Extract a raw value for the field from the data given
|
""" Extract a raw value for the field from the data given
|
||||||
using the key; if no corresponding value is found return
|
using the key; if no corresponding value is found return
|
||||||
the default.
|
the default. The value returned may then be turned
|
||||||
|
byt self.unmarshall() to the real (internal) value.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def marshall(value):
|
def marshall(value):
|
||||||
""" Return a string (possibly unicode) representation of the
|
""" Return a string (possibly unicode) representation of the
|
||||||
value given that may be used for editing.
|
value given that may be used for editing. In case of complex
|
||||||
|
or structured fields (list, mapping, object fields) the return
|
||||||
|
value may also be a structured object (typically a list or
|
||||||
|
mapping) built up form string (unicode) values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def display(value):
|
def display(value):
|
||||||
|
@ -220,9 +232,9 @@ class IFieldInstance(Interface):
|
||||||
value given that may be used for presentation.
|
value given that may be used for presentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def unmarshall(rawValue):
|
def unmarshall(inputValue):
|
||||||
""" Return the internal (real) value corresponding to the
|
""" Return the internal (real) value corresponding to the
|
||||||
raw value given.
|
input (external, raw) value given.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def validate(value, data=None):
|
def validate(value, data=None):
|
||||||
|
|
|
@ -7,15 +7,12 @@ A Basic API for Reports and Listings
|
||||||
TO DO...
|
TO DO...
|
||||||
|
|
||||||
>>> from zope import component
|
>>> from zope import component
|
||||||
>>> from zope.interface import directlyProvides
|
|
||||||
|
|
||||||
|
|
||||||
Listings
|
Listings
|
||||||
========
|
========
|
||||||
|
|
||||||
>>> from cybertools.reporter.data import DataSource
|
>>> from cybertools.reporter.data import DataSource
|
||||||
>>> from cybertools.reporter.resultset import ResultSet
|
|
||||||
>>> from cybertools.reporter.interfaces import IResultSet
|
|
||||||
|
|
||||||
Let's start with the Person class from the cybertools.organize package - we will
|
Let's start with the Person class from the cybertools.organize package - we will
|
||||||
then provide a listing of persons...
|
then provide a listing of persons...
|
||||||
|
@ -29,38 +26,42 @@ then provide a listing of persons...
|
||||||
>>> persons = DataSource([Person(f, s, date(*[int(d) for d in b.split('-')]))
|
>>> persons = DataSource([Person(f, s, date(*[int(d) for d in b.split('-')]))
|
||||||
... for f, s, b in pdata])
|
... for f, s, b in pdata])
|
||||||
|
|
||||||
|
>>> from cybertools.reporter.resultset import ResultSet, ContentRow
|
||||||
|
>>> from cybertools.reporter.interfaces import IResultSet, IRow
|
||||||
>>> component.provideAdapter(ResultSet)
|
>>> component.provideAdapter(ResultSet)
|
||||||
>>> rset = IResultSet(persons)
|
>>> component.provideAdapter(ContentRow, provides=IRow)
|
||||||
|
|
||||||
>>> len(list(rset.rows))
|
|
||||||
3
|
|
||||||
|
|
||||||
As we have not yet provided a schema for the result set the rows are
|
|
||||||
empty.
|
|
||||||
|
|
||||||
>>> r1 = rset.rows.next()
|
|
||||||
>>> list(r1.cells)
|
|
||||||
[]
|
|
||||||
|
|
||||||
So let's assign a schema to the result set.
|
|
||||||
|
|
||||||
>>> from cybertools.composer.schema.schema import Schema
|
>>> from cybertools.composer.schema.schema import Schema
|
||||||
>>> from cybertools.composer.schema.field import Field
|
>>> from cybertools.composer.schema.field import Field
|
||||||
>>> rset.schema = Schema(Field(u'firstName'), Field(u'lastName'), Field(u'birthDate'))
|
>>> from cybertools.composer.schema.field import FieldInstance, DateFieldInstance
|
||||||
>>> r1 = rset.rows.next()
|
>>> component.provideAdapter(FieldInstance)
|
||||||
>>> [c.text for c in r1.cells]
|
>>> component.provideAdapter(DateFieldInstance, name='date')
|
||||||
[u'Smith', u'John', u'1956-08-01']
|
|
||||||
|
>>> rset = IResultSet(persons)
|
||||||
|
>>> rset.schema = Schema(Field(u'firstName'), Field(u'lastName'),
|
||||||
|
... Field(u'birthDate', fieldType='date'))
|
||||||
|
|
||||||
|
>>> rows = list(rset.getRows())
|
||||||
|
>>> len(rows)
|
||||||
|
3
|
||||||
|
|
||||||
|
>>> for r in rows:
|
||||||
|
... print r.applyTemplate()
|
||||||
|
{u'lastName': u'John', u'birthDate': '1956-08-01', u'firstName': u'Smith'}
|
||||||
|
{u'lastName': u'David', u'birthDate': '1972-12-24', u'firstName': u'Waters'}
|
||||||
|
{u'lastName': u'Carla', u'birthDate': '1981-10-11', u'firstName': u'Myers'}
|
||||||
|
|
||||||
For the browser presentation we can also use a browser view providing
|
For the browser presentation we can also use a browser view providing
|
||||||
the result set with extended attributes:
|
the result set with extended attributes:
|
||||||
|
|
||||||
>>> #rsView = component.getMultiAdapter((context, TestRequest()), IBrowserView)
|
>>> #rsView = component.getMultiAdapter((rset, TestRequest()), IBrowserView)
|
||||||
|
|
||||||
|
|
||||||
The reporter package also includes facilities for sorting the rows in a
|
The reporter package also includes facilities for sorting the rows in a
|
||||||
result set and splitting a result into batches.
|
result set and splitting a result into batches.
|
||||||
|
|
||||||
Sorting
|
Sorting
|
||||||
-------
|
=======
|
||||||
|
|
||||||
|
|
||||||
Batching
|
Batching
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
xmlns:browser="http://namespaces.zope.org/browser"
|
xmlns:browser="http://namespaces.zope.org/browser"
|
||||||
i18n_domain="zope">
|
i18n_domain="zope">
|
||||||
|
|
||||||
|
<adapter
|
||||||
|
factory="cybertools.reporter.resultset.ContentRow"
|
||||||
|
provides="cybertools.reporter.interfaces.IRow"
|
||||||
|
/>
|
||||||
|
|
||||||
<include package=".browser" />
|
<include package=".browser" />
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
|
|
@ -55,6 +55,7 @@ class IRow(Interface):
|
||||||
class ICell(Interface):
|
class ICell(Interface):
|
||||||
""" A single cell of a listing or table.
|
""" A single cell of a listing or table.
|
||||||
"""
|
"""
|
||||||
|
# TODO: replace Cell by FieldInstance
|
||||||
|
|
||||||
field = Attribute(u'The schema field the cell belongs to.')
|
field = Attribute(u'The schema field the cell belongs to.')
|
||||||
|
|
||||||
|
|
|
@ -25,15 +25,18 @@ $Id$
|
||||||
|
|
||||||
# TODO: move the generic stuff to cybertools.reporter.result
|
# TODO: move the generic stuff to cybertools.reporter.result
|
||||||
|
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope.component import adapts
|
from zope.component import adapts
|
||||||
from zope.interface import implements
|
from zope.interface import Interface, implements
|
||||||
|
|
||||||
from cybertools.composer.schema.schema import Schema
|
from cybertools.composer.schema import Schema
|
||||||
|
from cybertools.composer.schema.instance import Instance
|
||||||
from cybertools.reporter.interfaces import IDataSource
|
from cybertools.reporter.interfaces import IDataSource
|
||||||
from cybertools.reporter.interfaces import IResultSet, IRow, ICell
|
from cybertools.reporter.interfaces import IResultSet, IRow, ICell
|
||||||
|
|
||||||
|
|
||||||
class Cell(object):
|
class Cell(object):
|
||||||
|
# TODO: replace Cell by FieldInstance
|
||||||
|
|
||||||
implements(ICell)
|
implements(ICell)
|
||||||
|
|
||||||
|
@ -61,7 +64,7 @@ class Cell(object):
|
||||||
url = urlTitle = u''
|
url = urlTitle = u''
|
||||||
|
|
||||||
|
|
||||||
class Row(object):
|
class Row(Instance):
|
||||||
|
|
||||||
implements(IRow)
|
implements(IRow)
|
||||||
|
|
||||||
|
@ -69,17 +72,33 @@ class Row(object):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.resultSet = resultSet
|
self.resultSet = resultSet
|
||||||
|
|
||||||
@property
|
@Lazy
|
||||||
def schema(self):
|
def schema(self):
|
||||||
return self.resultSet.schema
|
return self.resultSet.schema
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def fields(self):
|
||||||
|
return self.schema.fields
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cells(self):
|
def cells(self):
|
||||||
for f in self.resultSet.schema.fields:
|
for f in self.schema.fields:
|
||||||
rf = f.renderFactory or Cell
|
rf = f.renderFactory or Cell
|
||||||
yield rf(f, getattr(self.context, f.name), self)
|
yield rf(f, getattr(self.context, f.name), self)
|
||||||
|
|
||||||
|
|
||||||
|
class ContentRow(Instance):
|
||||||
|
""" A row adapter for standard content objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
implements(IRow)
|
||||||
|
adapts(Interface)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def fields(self):
|
||||||
|
return self.template.fields
|
||||||
|
|
||||||
|
|
||||||
class ResultSet(object):
|
class ResultSet(object):
|
||||||
|
|
||||||
implements(IResultSet)
|
implements(IResultSet)
|
||||||
|
@ -96,3 +115,9 @@ class ResultSet(object):
|
||||||
for o in iter(self.context):
|
for o in iter(self.context):
|
||||||
yield Row(o, self)
|
yield Row(o, self)
|
||||||
|
|
||||||
|
def getRows(self):
|
||||||
|
for o in iter(self.context):
|
||||||
|
row = IRow(o)
|
||||||
|
row.resultSet = self
|
||||||
|
row.template = self.schema
|
||||||
|
yield row
|
||||||
|
|
Loading…
Add table
Reference in a new issue