This module provides an experimental subclass of FieldSet to support zope.schema‘s schema. Simple validation is supported. Invariant is not supported.
Not all fields are supported. You can use TextLine, Text, Int, Bool, Float, Date, Datetime, Time, and Choice.
Here is a simple example. First we need a schema:
>>> class IPet(interface.Interface):
... name = schema.Text(title=u'Name', required=True)
... type = schema.TextLine(title=u'Type', required=True)
... age = schema.Int(min=1)
... owner = schema.TextLine(title=u'Owner')
... birthdate = schema.Date(title=u'Birth date')
... colour = schema.Choice(title=u'Colour',
... values=['Brown', 'Black'])
... friends = schema.List(title=u'Friends', value_type=schema.Choice(['cat', 'dog', 'bulldog']))
Initialize FieldSet with schema:
>>> fs = FieldSet(IPet)
Create a class to store values. If your class does not implement the form interface the FieldSet will generate an adapter for you:
>>> class Pet(FlexibleDict):pass
>>> p = Pet(name='dewey', type='cat', owner='gawel', friends=['cat', 'dog'])
>>> fs = fs.bind(p)
Fields are aware of schema attributes:
>>> fs.name.is_required()
True
We can use the form:
>>> print fs.render().strip() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
<div>
<label class="field_req" for="Pet--name">Name</label>
<textarea id="Pet--name" name="Pet--name">dewey</textarea>
</div>
<script type="text/javascript">
//<![CDATA[
document.getElementById("Pet--name").focus();
//]]>
</script>
<div>
<label class="field_req" for="Pet--type">Type</label>
<input id="Pet--type" name="Pet--type" type="text" value="cat" />
</div>
<div>
<label class="field_req" for="Pet--age">age</label>
<input id="Pet--age" name="Pet--age" type="text" />
</div>
<div>
<label class="field_req" for="Pet--owner">Owner</label>
<input id="Pet--owner" name="Pet--owner" type="text" value="gawel" />
</div>
<div>
<label class="field_req" for="Pet--birthdate">Birth date</label>
<span id="Pet--birthdate"><select id="Pet--birthdate__month" name="Pet--birthdate__month">
<option value="MM">Month</option>
<option value="1">January</option>
<option value="2">February</option>
...
<option value="31">31</option>
</select>
<input id="Pet--birthdate__year" maxlength="4" name="Pet--birthdate__year" size="4" type="text" value="YYYY" /></span>
</div>
<div>
<label class="field_req" for="Pet--colour">Colour</label>
<select id="Pet--colour" name="Pet--colour">
<option value="Brown">Brown</option>
<option value="Black">Black</option>
</select>
</div>
<div>
<label class="field_req" for="Pet--friends">Friends</label>
<select id="Pet--friends" multiple="multiple" name="Pet--friends">
<option value="bulldog">bulldog</option>
<option selected="selected" value="dog">dog</option>
<option selected="selected" value="cat">cat</option>
</select>
</div>
Ok, let’s assume that validation and syncing works:
>>> fs.configure(include=[fs.name]) >>> fs.rebind(p, data={'Pet--name':'minou'}) >>> fs.validate() True >>> fs.sync() >>> fs.name.value 'minou' >>> p.name 'minou'>>> fs.configure(include=[fs.age]) >>> fs.rebind(p, data={'Pet--age':'-1'}) >>> fs.validate() False >>> fs.age.errors [u'Value is too small'] >>> fs.configure(include=[fs.colour]) >>> fs.rebind(p, data={'Pet--colour':'Yellow'}) >>> fs.validate() False >>> fs.colour.errors [u'Constraint not satisfied']>>> fs.rebind(p, data={'Pet--colour':'Brown'}) >>> fs.validate() True >>> fs.sync() >>> fs.colour.value 'Brown' >>> p.colour 'Brown'
Looks nice ! Let’s use the grid:
>>> grid = Grid(IPet)
>>> grid = grid.bind([p])
>>> print grid.render().strip() #doctest: +ELLIPSIS
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>age</th>
<th>Owner</th>
<th>Birth date</th>
<th>Colour</th>
<th>Friends</th>
</tr>
...
<tr class="even">
<td>
<textarea id="Pet--name" name="Pet--name">minou</textarea>
</td>
<td>
<input id="Pet--type" name="Pet--type" type="text" value="cat" />
</td>
<td>
<input id="Pet--age" name="Pet--age" type="text" />
</td>
<td>
<input id="Pet--owner" name="Pet--owner" type="text" value="gawel" />
</td>
<td>
<span id="Pet--birthdate"><select id="Pet--birthdate__month" name="Pet--birthdate__month">
<option value="MM">Month</option>
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select id="Pet--birthdate__day" name="Pet--birthdate__day">
<option value="DD">Day</option>
<option value="1">1</option>
...
<option value="31">31</option>
</select>
<input id="Pet--birthdate__year" maxlength="4" name="Pet--birthdate__year" size="4" type="text" value="YYYY" /></span>
</td>
<td>
<select id="Pet--colour" name="Pet--colour">
<option selected="selected" value="Brown">Brown</option>
<option value="Black">Black</option>
</select>
</td>
<td>
<select id="Pet--friends" multiple="multiple" name="Pet--friends">
<option value="bulldog">bulldog</option>
<option selected="selected" value="dog">dog</option>
<option selected="selected" value="cat">cat</option>
</select>
</td>
</tr>
</tbody>
Field aware of zope schema. See formalchemy.fields.AbstractField for full api.
Create a new Field object.
field name
data type, from formalchemy.types (Integer, Float, String, Binary, Boolean, Date, DateTime, Time) or a custom type
default value. If value is a callable, it will be passed the current bound model instance when the value is read. This allows creating a Field whose value depends on the model once, then binding different instances to it later.
FormAlchemy use a _pk attribute to identify objects. You can use this property to bind another attribute as a primary key:
>>> class Content(object):
... _pk = Pk()
... __name__ = 'primary_key'
>>> content = Content()
>>> content._pk
'primary_key'
>>> content._pk = 'another_key'
>>> content.__name__
'another_key'
>>> class Content(object):
... _pk = Pk('uid')
... uid = 'primary_key'
>>> content = Content()
>>> content._pk
'primary_key'
>>> fields._pk(content)
'primary_key'
A flexible object to easy adapt most python classes:
>>> obj = FlexibleModel(owner='gawel')
>>> obj.owner == obj.get('owner') == obj['owner'] == 'gawel'
True
>>> obj._pk is None
True
If your object provide an uuid attribute then FormAlchemy will use it has primary key:
>>> import uuid
>>> obj = FlexibleModel(uuid=uuid.uuid4())
>>> obj._pk is None
False
like FlexibleModel but inherit from dict:
>>> obj = FlexibleDict(owner='gawel')
>>> obj.owner == obj.get('owner') == obj['owner'] == 'gawel'
True
>>> isinstance(obj, dict)
True
>>> 'owner' in obj
True
return a new FlexibleModel or FlexibleDict factory who provide iface:
>>> class ITitle(interfaces.Interface):
... title = schema.TextLine(title=u'title')
>>> factory = gen_model(ITitle)
>>> adapted = factory()
>>> ITitle.providedBy(adapted)
True
>>> class Title(object):
... title = None
>>> obj = Title()
>>> adapted = factory(obj)
>>> adapted.context is obj
True
>>> adapted.title = 'my title'
>>> obj.title
'my title'
>>> obj = dict()
>>> adapted = factory(obj)
>>> adapted.context is obj
True
>>> adapted.title = 'my title'
>>> obj['title']
'my title'