add keytable stuff to grid-like field definitions

This commit is contained in:
Helmut Merz 2011-12-23 10:30:06 +01:00
parent 3e5b4d2bff
commit 4c2f79d929
3 changed files with 130 additions and 23 deletions

View file

@ -2,8 +2,6 @@
Schema and Field Management
===========================
($Id$)
>>> from cybertools.composer.schema import Schema
>>> from cybertools.composer.schema import Field
@ -193,3 +191,58 @@ Macros / renderers
[u'field', u'field_spacer', u'fields', u'form', u'input_checkbox',
u'input_date', u'input_dropdown', u'input_fileupload', u'input_html',
u'input_list', u'input_password', u'input_textarea', u'input_textline']
Special Field Types
===================
Grids, Records, Key Tables
--------------------------
>>> from cybertools.composer.schema.grid.field import KeyTableFieldInstance
>>> ktfield = Field('data')
>>> ktfield.column_types = [zope.schema.Text(__name__='key', title=u'Key',),
... zope.schema.Text(__name__='value', title=u'Value')]
>>> ktfi = KeyTableFieldInstance(ktfield)
>>> ktfi.unmarshall([dict(key='0001', value='First row')])
{u'0001': [u'First row']}
>>> ktfi.marshall({u'0001': [u'First row']})
[{'value': u'First row', 'key': u'0001'}]
Now with some real stuff, using a field instance that takes the column types
from the context object of the edit form.
>>> from cybertools.composer.schema.grid.interfaces import KeyTable
>>> from cybertools.composer.schema.grid.field import \
... ContextBasedKeyTableFieldInstance
>>> component.provideAdapter(ContextBasedKeyTableFieldInstance, name='keytable')
>>> class IDataTable(Interface):
... title = zope.schema.TextLine(title=u'Title', required=False)
... columnNames = zope.schema.List(title=u'Column Names', required=False)
... data = KeyTable(title=u'Data', required=False)
>>> IDataTable['columnNames'].nostore = True
>>> class DataTable(object):
... implements(IDataTable)
... def __init__(self, title, columnNames):
... self.title = title
... self.columnNames = columnNames
>>> dt = DataTable('Account Types', ['identifier', 'label', 'info'])
>>> input = dict(title='Account Types',
... columnNames=['identifier', 'label', 'info'],
... data=[dict(identifier='0001', label='Standard', info='')],
... action='update')
>>> form = Form(dt, TestRequest(form=input))
>>> form.interface = IDataTable
>>> form.nextUrl = 'dummy_url'
>>> form.update()
False
>>> dt.data
{u'0001': [u'Standard', u'']}

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,8 +18,6 @@
"""
Field and field instance classes for grids.
$Id$
"""
from zope import component
@ -30,7 +28,7 @@ from zope.cachedescriptors.property import Lazy
import zope.schema
from cybertools.composer.schema.factory import createField
from cybertools.composer.schema.field import ListFieldInstance
from cybertools.composer.schema.field import Field, ListFieldInstance
from cybertools.composer.schema.interfaces import IField, IFieldInstance
from cybertools.composer.schema.interfaces import fieldTypes, undefined
from cybertools.util.format import toStr, toUnicode
@ -91,6 +89,13 @@ class GridFieldInstance(ListFieldInstance):
return []
result = []
rows = json.loads(value)['items']
for row in rows:
item = self.unmarshallRow(row)
if item:
result.append(item)
return result
def dummy(self):
for row in rows:
item = {}
empty = True
@ -106,6 +111,18 @@ class GridFieldInstance(ListFieldInstance):
result.append(item)
return result
def unmarshallRow(self, row):
item = {}
for fi in self.columnFieldInstances:
value = fi.unmarshall(row[fi.name])
if isinstance(value, basestring):
value = value.strip()
if fi.default is not None:
if value == fi.default:
continue
item[fi.name] = value
return item
class RecordsFieldInstance(GridFieldInstance):
@ -123,19 +140,49 @@ class RecordsFieldInstance(GridFieldInstance):
value = []
result = []
for row in value:
item = {}
empty = True
for fi in self.columnFieldInstances:
value = row[fi.name]
if isinstance(value, basestring):
value = value.strip()
value = fi.unmarshall(value)
item[fi.name] = value
if fi.default is not None:
if value and value != fi.default:
empty = False
elif value:
empty = False
if not empty:
item = self.unmarshallRow(row)
if item:
result.append(item)
return result
class KeyTableFieldInstance(RecordsFieldInstance):
@Lazy
def keyName(self):
return self.columnTypes[0].name
@Lazy
def dataNames(self):
return [f.name for f in self.columnTypes[1:]]
def marshall(self, value):
result = []
if not value:
return result
for k, v in value.items():
item = {self.keyName: k}
for idx, name in enumerate(self.dataNames):
item[name] = v[idx]
result.append(item)
return result
def unmarshall(self, value):
if not value:
value = {}
result = {}
for row in value:
item = self.unmarshallRow(row)
if item:
result[item.pop(self.keyName)] = [item.get(name)
for name in self.dataNames]
return result
class ContextBasedKeyTableFieldInstance(KeyTableFieldInstance):
@Lazy
def columnTypes(self):
obj = self.clientInstance.context
return [Field(name) for name in obj.columnNames]

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,8 +18,6 @@
"""
Grid field definition.
$Id$
"""
from zope import schema
@ -50,3 +48,12 @@ class Records(Grid):
u'A series of records or rows.',
instanceName='records',))
class KeyTable(Grid):
__typeInfo__ = ('keytable',
FieldType('keytable', 'keytable',
u'A dictionary of records or rows the first '
u'column of which represents the key.',
instanceName='keytable',))