248 lines
8.1 KiB
Text
248 lines
8.1 KiB
Text
===========================
|
|
Schema and Field Management
|
|
===========================
|
|
|
|
>>> from cybertools.composer.schema import Schema
|
|
>>> from cybertools.composer.schema import Field
|
|
|
|
|
|
Working with predefined schemas
|
|
===============================
|
|
|
|
We start with setting up a schema with fields.
|
|
|
|
>>> serviceSchema = Schema(
|
|
... Field(u'title', renderFactory=None),
|
|
... Field(u'description'),
|
|
... Field(u'start'),
|
|
... Field(u'end'),
|
|
... Field(u'capacity'),
|
|
... )
|
|
|
|
For using a schema we need some class that we can use for creating
|
|
objects.
|
|
|
|
>>> class Service(object):
|
|
... pass
|
|
|
|
The schema will be connected with an object via an instance adapter.
|
|
In addition, we need a field instance adapter that cares for the
|
|
correct conversion of input data to context attributes.
|
|
|
|
>>> from cybertools.composer.schema.instance import Editor
|
|
>>> from cybertools.composer.schema.field import FieldInstance
|
|
>>> from zope import component
|
|
>>> component.provideAdapter(Editor, (Service,), name="service.edit")
|
|
>>> component.provideAdapter(FieldInstance)
|
|
|
|
>>> srv = Service()
|
|
>>> inst = component.getAdapter(srv, name='service.edit')
|
|
>>> inst.template = serviceSchema
|
|
>>> inst.applyTemplate(data=dict(title='Service', capacity='30'))
|
|
<...FormState object ...>
|
|
|
|
>>> srv.title, srv.description, srv.capacity
|
|
(u'Service', u'', u'30')
|
|
|
|
Field types
|
|
-----------
|
|
|
|
>>> from cybertools.composer.schema.interfaces import fieldTypes
|
|
>>> sorted(t.token for t in fieldTypes)
|
|
['checkbox', 'checkboxes', 'date', 'display', 'dropdown', 'email',
|
|
'explanation', 'fileupload', 'float', 'heading', 'html', 'list', 'number',
|
|
'password', 'radiobuttons', 'spacer', 'textarea', 'textline']
|
|
|
|
>>> from zope.schema.vocabulary import SimpleVocabulary
|
|
>>> textFieldTypes = SimpleVocabulary([t for t in fieldTypes if t.token in
|
|
... ('textline', 'textarea',)])
|
|
>>> sorted(t.token for t in textFieldTypes)
|
|
['textarea', 'textline']
|
|
|
|
Dynamic default values
|
|
----------------------
|
|
|
|
>>> idField = Field(u'id', default='user/title|string:???', defaultValueType='tales')
|
|
>>> idField.getDefaultValue()
|
|
'???'
|
|
|
|
|
|
Creating a schema from an interface
|
|
===================================
|
|
|
|
>>> from zope.interface import Interface, implements
|
|
>>> import zope.schema
|
|
>>> from cybertools.composer.schema.factory import SchemaFactory
|
|
>>> component.provideAdapter(SchemaFactory)
|
|
|
|
>>> class IPerson(Interface):
|
|
... firstName = zope.schema.TextLine(title=u'First name')
|
|
... lastName = zope.schema.TextLine(title=u'Last name')
|
|
... age = zope.schema.Int(title=u'Age')
|
|
|
|
>>> class Person(object):
|
|
... implements(IPerson)
|
|
... def __init__(self, firstName=u'', lastName=u'', age=None):
|
|
... self.firstName, self.lastName, self.age = firstName, lastName, age
|
|
|
|
>>> from cybertools.composer.schema.interfaces import ISchemaFactory
|
|
>>> factory = ISchemaFactory(Person())
|
|
|
|
>>> schema = factory(IPerson)
|
|
>>> for f in schema.fields:
|
|
... print f.name, f.title, f.fieldType
|
|
firstName First name textline
|
|
lastName Last name textline
|
|
age Age number
|
|
|
|
Using a more specialized schema factory
|
|
---------------------------------------
|
|
|
|
>>> class PersonSchemaFactory(SchemaFactory):
|
|
... def __call__(self, interface, **kw):
|
|
... schema = super(PersonSchemaFactory, self).__call__(interface)
|
|
... if 'firstName' in schema.fields.keys():
|
|
... del schema.fields['firstName'] # don't show first name
|
|
... return schema
|
|
>>> component.provideAdapter(PersonSchemaFactory, (IPerson,))
|
|
|
|
>>> factory = ISchemaFactory(Person())
|
|
>>> schema = factory(IPerson)
|
|
>>> for f in schema.fields:
|
|
... print f.name, f.title, f.fieldType
|
|
lastName Last name textline
|
|
age Age number
|
|
|
|
|
|
Access and update a context object using a schema-based form
|
|
============================================================
|
|
|
|
>>> from zope.publisher.browser import TestRequest
|
|
>>> from cybertools.composer.schema.browser.form import Form
|
|
|
|
We first have to provide adapters for special field types ('number' in
|
|
this case) and an instance adapter that manages the access to the
|
|
context object.
|
|
|
|
>>> from cybertools.composer.schema.field import NumberFieldInstance
|
|
>>> component.provideAdapter(NumberFieldInstance, name='number')
|
|
|
|
>>> from cybertools.composer.schema.instance import Instance
|
|
>>> component.provideAdapter(Instance)
|
|
|
|
>>> person = Person(u'John', u'Miller', 33)
|
|
|
|
Note that the first name is not shown as we excluded it via the schema
|
|
factory above. The age field is a number, but is shown here as a
|
|
string as the instance is accessed using 'edit' mode, i.e. provide
|
|
data suitable for showing on an HTML form.
|
|
|
|
>>> form = Form(person, TestRequest())
|
|
>>> form.interface = IPerson
|
|
>>> form.data
|
|
{'lastName': u'Miller', 'age': '33'}
|
|
|
|
For editing we have to provide another instance adapter.
|
|
|
|
>>> from cybertools.composer.schema.instance import Editor
|
|
>>> component.provideAdapter(Editor, name='editor')
|
|
|
|
>>> input = dict(lastName='Miller', age='40', action='update')
|
|
>>> request = TestRequest(form=input)
|
|
>>> form = Form(person, request)
|
|
>>> form.interface = IPerson
|
|
>>> form.nextUrl = 'dummy_url' # avoid hassle with IAbsoluteURL view...
|
|
|
|
>>> form.update()
|
|
False
|
|
|
|
>>> person.age
|
|
40
|
|
|
|
Create a new object using a schema-based form
|
|
---------------------------------------------
|
|
|
|
>>> from cybertools.composer.schema.browser.form import CreateForm
|
|
>>> container = dict()
|
|
|
|
>>> input = dict(lastName=u'Smith', age='28', action='update')
|
|
>>> form = CreateForm(container, TestRequest(form=input))
|
|
>>> form.interface = IPerson
|
|
>>> form.factory = Person
|
|
|
|
>>> form.nextUrl = 'dummy_url' # avoid hassle with IAbsoluteURL view...
|
|
>>> form.getName = lambda x: x.lastName.lower()
|
|
|
|
>>> form.data
|
|
{'lastName': u'Smith', 'age': '28'}
|
|
|
|
>>> form.update()
|
|
False
|
|
|
|
>>> p2 = container['smith']
|
|
>>> p2.lastName, p2.age
|
|
(u'Smith', 28)
|
|
|
|
Macros / renderers
|
|
------------------
|
|
|
|
>>> fieldRenderers = form.fieldRenderers
|
|
>>> sorted(fieldRenderers.keys())
|
|
[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'']}
|